<?xml version="1.0" encoding="UTF-8"?> <ruleset name="jpinpoint-rules" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd"> <description> jPinpoint rules for responsible Java coding, sponsored by Rabobank. Uses PMD-7. </description> <!-- IMPORTANT NOTICE: The content of this file is generated. Do not edit this file directly since changes may be lost when this file is regenerated! --> <!-- BEGIN Included file 'common.xml' --> <rule name="AvoidCDIReferenceLeak" language="java" message="Explicit CDI references need to be destroyed otherwise they leak." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#pml05"> <description>Problem: A proxy object is created by Contexts and Dependency Injection (CDI) for explicit references, they are not de-referenced implicitly and become a memory leak. Solution: Destroy the reference explicitly. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,memory,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[@MethodName='select']/MethodCall[@MethodName='current']/TypeExpression/ClassType[@SimpleName='CDI' and not (ancestor::MethodDeclaration//TryStatement/FinallyClause //MethodCall[@MethodName='destroy'][ArgumentList/VariableAccess[@Name=ancestor:: MethodDeclaration//(VariableDeclarator|AssignmentExpression)[.//MethodCall[@MethodName='current']/TypeExpression/ClassType[@SimpleName='CDI']]/(VariableId|VariableAccess)/@Name]] [MethodCall[@MethodName='current']/TypeExpression/ClassType[@SimpleName='CDI']]) ] ]]></value> </property> </properties> <example> <![CDATA[ public class CDIStuff { private void bad() { MyClass o = CDI.current().select(MyClass.class).get(); o.doStuff(); // bad - missing destroy in finally } private void good() { MyClass o = CDI.current().select(MyClass.class).get(); try { o.doStuff(); } finally { CDI.current().destroy(o); // good - destroy properly } } } ]]> </example> </rule> <rule name="AvoidCalendar" language="java" message="A Calendar is inefficient in memory usage." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#imu01"> <description>Problem: A Calendar is a heavyweight object and expensive to create. Solution: Use Date, Java 8+ java.time.[Local/Zoned]DateTime. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="xpath"> <value><![CDATA[ //ClassType[pmd-java:typeIs('java.util.Calendar')] ]]></value> </property> <property name="tags" value="jpinpoint-rule,memory,performance,sustainability-medium" type="String" description="classification"/> </properties> <example> <![CDATA[ public class CalendarStuff { Calendar field1; // bad private Calendar bad1() { return Calendar.getInstance(); } private Date good1a() { return new Date(); // now } private LocalDateTime good1b() { return LocalDateTime.now(); } } ]]> </example> </rule> <rule name="AvoidConstantsInInterface" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Interface defines constants. It may expose implementation details." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#voedos04"> <description>Interface defines constants. Problem: Possibly exposes implementation details. Solution: Make it a Class which cannot be instantiated, or an Enum. Use static imports. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="jpinpoint-rule,bad-practice" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassDeclaration[@Interface=true()]/ClassBody/FieldDeclaration ]]></value> </property> </properties> <example> <![CDATA[ public interface AnimalConsts { public static final Dog DOG = new Dog(); //bad } public class AnimalUtil { private AnimalUtil() {} public static final Dog DOG = new Dog(); // good } public enum Animal { DOG // good } ]]> </example> </rule> <rule name="AvoidDecimalAndChoiceFormatAsField" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Avoid using DecimalFormat or ChoiceFormat as field since it is thread-unsafe." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#idtf01"> <description>Problem: java.text.NumberFormat: DecimalFormat and ChoiceFormat are thread-unsafe. Solution: usual solution is to create a new local one when needed in a method. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //FieldDeclaration/ClassType[pmd-java:typeIs('java.text.NumberFormat')] ]]></value> </property> </properties> <example> <![CDATA[ public class Foo { public static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("###.###"); // bad public void bar() { NumberFormat format = new DecimalFormat("###.###"); // good } } ]]> </example> </rule> <rule name="AvoidDuplicateAssignmentsInCases" message="Avoid duplicate assignments in different switch cases" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#SSC01"> <description> Problem: Potential bug: expected to have different assignments in different cases. Solution: assign different values in different cases, common assignments should be taken out of the switch. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,suspicious" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ (:-- assigned to same var in a previous switch case --:) //SwitchStatement//SwitchFallthroughBranch/ExpressionStatement[AssignmentExpression[not(pmd-java:typeIs('boolean'))]/VariableAccess[1]/@Name= ../preceding-sibling::SwitchFallthroughBranch//preceding-sibling::ExpressionStatement/AssignmentExpression/VariableAccess[1]/@Name and ( (:-- assigning same var --:) AssignmentExpression/VariableAccess[2]/@Name= ../preceding-sibling::SwitchFallthroughBranch//preceding-sibling::ExpressionStatement/AssignmentExpression/VariableAccess[2]/@Name or (:-- assigning same literal --:) AssignmentExpression/(StringLiteral|NumericLiteral)/@Image = ../preceding-sibling::SwitchFallthroughBranch//preceding-sibling::ExpressionStatement/AssignmentExpression/(StringLiteral|NumericLiteral)/@Image or (:-- assigning constructor with same argument--:) AssignmentExpression/ConstructorCall/ArgumentList[@Size=1]/StringLiteral/@ConstValue = ../preceding-sibling::SwitchFallthroughBranch//preceding-sibling::ExpressionStatement/AssignmentExpression/ConstructorCall/ArgumentList[@Size=1]/StringLiteral/@ConstValue or (:-- assigning same argumentless method possibly on a field of a type --:) AssignmentExpression/MethodCall[ArgumentList[@Size=0]]/concat(@MethodName, .//FieldAccess/@Name, .//FieldAccess//ClassType/@SimpleName) = ../preceding-sibling::SwitchFallthroughBranch//preceding-sibling::ExpressionStatement/AssignmentExpression/MethodCall[ArgumentList[@Size=0]]/concat(@MethodName, .//FieldAccess/@Name, .//FieldAccess//ClassType/@SimpleName) ) (:--default case allowed to have duplicates --:) and not (preceding-sibling::SwitchLabel[@Default=true()])] ]]> </value> </property> </properties> <example> <![CDATA[ switch (type) { case "One": xmlNsExtValue = "same"; schemaLocation = 1; startElement = N_INITN; extra = new String("alsoSame"); extra = dummy; extra = toString(); break; case "Two": xmlNsValue = LINT008; // good xmlNsExtValue = "same"; // bad schemaLocation = 1; // bad startElement = Y_INITN; // good extra = new String("alsoSame"); // bad break; default: throw new IllegalArgumentException("Type " + type + " is not supported."); } ]]> </example> </rule> <rule name="AvoidExposingMutableRecordState" language="java" message="Avoid exposing mutable state of the record. Use copyOf in the (compact) constructor." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#voedos06"> <description>Problem: Internal state can be modified from outside the record, through the implicit accessor method or by the caller of the constructor. Risk of thread-unsafety. Solution: Use the record compact constructor to defensively copy the (possibly) mutable object such as a List, Set or Map, e.g. with List.copyOf(). Make sure to avoid NullPointerExceptions for null values in List.copyOf() methods. Beware that contained objects in collections can itself be mutable. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //RecordDeclaration//RecordComponent/ClassType[(pmd-java:typeIs('java.util.Collection') or pmd-java:typeIs('java.util.Map')) and not(../VariableId/@Name = ancestor::RecordDeclaration/RecordBody/(CompactConstructorDeclaration|ConstructorDeclaration) //AssignmentExpression[(VariableAccess|FieldAccess)/@Name = .//MethodCall[contains(lower-case(@MethodName), 'copyof')] /ArgumentList//(VariableAccess|FieldAccess)/@Name]/(VariableAccess|FieldAccess)/@Name) ]/.. ]]></value> </property> </properties> <example> <![CDATA[ record BadRecord(String name, List<String> list) { } record GoodRecord(String name, List<String> list) { public GoodRecord { list = list != null ? List.copyOf(list) : List.of(); } } ]]> </example> </rule> <rule name="AvoidImplicitlyRecompilingRegex" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="String regex method, Pattern.matches or FileSystem.getPathMatcher is used. Implicitly compiles a regex pattern, can be expensive." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ireu01"> <description>A regular expression is compiled implicitly on every invocation. Problem: this can be expensive, depending on the length of the regular expression. Solution: Compile the regex pattern only once and assign it to a private static final Pattern field. java.util.Pattern objects are thread-safe, so they can be shared among threads. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (:- method calls for non-short regex literals and used fields defined with non-short regex literal, or not defined as field -:) //MethodDeclaration//MethodCall[pmd-java:matchesSig('java.lang.String#replaceAll(java.lang.String,java.lang.String)') or pmd-java:matchesSig('java.lang.String#replaceFirst(java.lang.String,java.lang.String)') or pmd-java:matchesSig('java.util.regex.Pattern#matches(java.lang.String,java.lang.CharSequence)') or pmd-java:matchesSig('java.lang.String#split(java.lang.String)') or pmd-java:matchesSig('java.lang.String#matches(java.lang.String)') or pmd-java:matchesSig('java.nio.file.FileSystem#getPathMatcher(java.lang.String)') ] /ArgumentList/*[1][(self::StringLiteral and string-length(@Image) > 5 and (matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+'))) or self::VariableAccess and @Name=ancestor::ClassBody/FieldDeclaration/VariableDeclarator[StringLiteral[string-length(@Image) > 5 and (matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+'))] or not(StringLiteral)]/VariableId/@Name] ]]></value> </property> </properties> <example> <![CDATA[ String bad_replaceInnerLineBreakBySpace() { return text.replaceAll("([^\\.\\n])\\n", "$1 "); // bad } private static final Pattern INNER_LINE_BREAK_PATTERN = Pattern.compile("([^\\.\\n])\\n"); // good String good_replaceInnerLineBreakBySpace() { return INNER_LINE_BREAK_PATTERN.matcher(text).replaceAll("$1 "); // good } ]]> </example> </rule> <rule name="AvoidInMemoryStreamingDefaultConstructor" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="The default capacity or smaller is used for ByteArrayOutputStream or StringWriter, it usually needs expensive expansions." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isio01"> <description>Problem: The default constructor of ByteArrayOutputStream creates a 32 bytes initial capacity and for StringWriter 16 chars. Problem: Such a small buffer as capacity usually needs several expensive expansions. Solution: Pre-size the ByteArrayOutputStream or StringWriter with an initial capacity such that an expansion is not needed in most cases, typically much larger than 32, for instance 4096. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ConstructorCall[pmd-java:matchesSig('java.io.ByteArrayOutputStream#new()') or pmd-java:matchesSig('java.io.StringWriter#new()') or ((pmd-java:matchesSig('java.io.ByteArrayOutputStream#new(int)') and (ArgumentList/NumericLiteral/@ValueAsInt <=32 or ArgumentList/VariableAccess/@Name = ancestor::ClassBody/FieldDeclaration/VariableDeclarator[NumericLiteral/@ValueAsInt <= 32]/VariableId/@Name)) or (pmd-java:matchesSig('java.io.StringWriter#new(int)') and (ArgumentList/NumericLiteral/@ValueAsInt <=16 or ArgumentList/VariableAccess/@Name = ancestor::ClassBody/FieldDeclaration/VariableDeclarator[NumericLiteral/@ValueAsInt <= 16]/VariableId/@Name)) ) ] ]]></value> </property> </properties> <example> <![CDATA[ class Bad { public static void bad() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); //bad StringWriter sw = new StringWriter(); //bad baos = new ByteArrayOutputStream(32); //bad - not larger than default } } class Good { public static void good() { ByteArrayOutputStream baos = new ByteArrayOutputStream(8192); // 8 kiB StringWriter sw = new StringWriter(2048); } } ]]> </example> </rule> <rule name="AvoidInfiniteRecursion" language="java" message="Avoid recursive calls without a proper stop condition." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaPerformance.md#ipf01"> <description>Problem: If a method calls itself (recursion), and it doesn't have a proper and guaranteed stop condition, it may become an infinite loop and result in an OutOfMemoryError or StackOverflowError, and high CPU usage. Solution: Limit the number of recursive calls: use a counter to count up-to or down-from a maximum number of calls, for every recursive call, and stop when the maximum is reached. This maximum should not be a large number. Or better yet, rewrite into iterations for better performance and avoiding errors. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="xpath"> <value><![CDATA[ //MethodDeclaration[not(.//IfStatement[ ((.//UnaryExpression[@Operator=('++', '--')]|.//InfixExpression[@Operator=('+', '-')]/NumericLiteral[@ValueAsInt=1]|.//AssignmentExpression[@Operator=('+=', '-=')]/NumericLiteral[@ValueAsInt=1]) or .//MethodCall[contains(@MethodName,'crement')]/VariableAccess[pmd-java:typeIs('java.lang.Number')]) and .//InfixExpression[@Operator=('>', '>=','<', '<=')] (: same method name :) ])]//MethodCall[@MethodName = ancestor::MethodDeclaration/@Name] (: only called on implicit or explicit this :) [(ArgumentList and count(*) = 1) or (ArgumentList and ThisExpression and count(*) = 2)] (: only if the number of parameters match: not an overloaded method with difference in numbers :) [ArgumentList/@Size = ancestor::MethodDeclaration/FormalParameters/@Size] (: not if any parameter is used in the call in another position :) [not((ArgumentList/*[self::VariableAccess and ((position() != index-of(ancestor::MethodDeclaration/FormalParameters/FormalParameter/VariableId/@Name, concat('', @Name))))]))] (: we check the first 3 arguments on type, which is complicated :) (: if the 1st parameter (if it exists) :) (: its name equals the name of the 1st argument of the called method :) [concat(ArgumentList/*[1]/@Name, '') = ancestor::MethodDeclaration/FormalParameters/FormalParameter[1]/VariableId/@Name or ArgumentList/MethodCall/TypeExpression/ClassType/@SimpleName = ancestor::MethodDeclaration/FormalParameters/FormalParameter[1]/ClassType/@SimpleName or ancestor::MethodDeclaration/FormalParameters[@Size = 0 or FormalParameter[1][( (: or its classType is equal to the local var classtype :) (ClassType/@SimpleName|PrimitiveType/@Kind) = ancestor::MethodDeclaration//LocalVariableDeclaration[ (: which name equals to the name of the 1st argument of the method call :) .//VariableId/@Name = ancestor::MethodDeclaration//MethodCall[@MethodName = ancestor::MethodDeclaration/@Name]/ArgumentList/VariableAccess[1]/@Name ][ (: and which classType is equal to the classtype of the first parameter :) ((ClassType/@SimpleName|PrimitiveType/@Kind) = ancestor::MethodDeclaration/FormalParameters/FormalParameter[1]/(ClassType/@SimpleName|PrimitiveType/@Kind)) ]/(ClassType/@SimpleName|PrimitiveType/@Kind) or (: or its classType is equal to the field classtype :) (ClassType/@SimpleName|PrimitiveType/@Kind) = ancestor::ClassBody//FieldDeclaration[ (: which name equals to the name of the 1st argument of the method call :) .//VariableId/@Name = ..//MethodDeclaration//MethodCall[@MethodName = ancestor::MethodDeclaration/@Name]/ArgumentList/VariableAccess[1]/@Name ][ (: and which classType is equal to the classtype of the first parameter :) ((ClassType/@SimpleName|PrimitiveType/@Kind) = ..//MethodDeclaration/FormalParameters/FormalParameter[1]/(ClassType/@SimpleName|PrimitiveType/@Kind)) ]/(ClassType/@SimpleName|PrimitiveType/@Kind)) ]]] (: if the 2nd parameter (if it exists) :) [concat(ArgumentList/*[2]/@Name, '') = ancestor::MethodDeclaration/FormalParameters/FormalParameter[2]/VariableId/@Name or ancestor::MethodDeclaration/FormalParameters[@Size <= 1 or FormalParameter[2][( (: or its classType is equal to the local var classtype :) (ClassType/@SimpleName|PrimitiveType/@Kind) = (ancestor::MethodDeclaration//LocalVariableDeclaration|ancestor::ClassBody//FieldDeclaration)[ (: which name equals to the name of the 2nd argument of the method call :) .//VariableId/@Name = ancestor::MethodDeclaration//MethodCall[@MethodName = ancestor::MethodDeclaration/@Name]/ArgumentList/VariableAccess[2]/@Name][ (: and which classType is equal to the classtype of the 2nd parameter :) ((ClassType/@SimpleName|PrimitiveType/@Kind) = ancestor::MethodDeclaration/FormalParameters/FormalParameter[2]/(ClassType/@SimpleName|PrimitiveType/@Kind)) ]/(ClassType/@SimpleName|PrimitiveType/@Kind) or (: or its classType is equal to the field classtype :) (ClassType/@SimpleName|PrimitiveType/@Kind) = ancestor::ClassBody//FieldDeclaration[ (: which name equals to the name of the 2nd argument of the method call :) .//VariableId/@Name = ..//MethodDeclaration//MethodCall[@MethodName = ancestor::MethodDeclaration/@Name]/ArgumentList/VariableAccess[2]/@Name ][ (: and which classType is equal to the classtype of the 2nd parameter :) ((ClassType/@SimpleName|PrimitiveType/@Kind) = ..//MethodDeclaration/FormalParameters/FormalParameter[2]/(ClassType/@SimpleName|PrimitiveType/@Kind)) ]/(ClassType/@SimpleName|PrimitiveType/@Kind)) ]]] (: if the 3rd parameter (if it exists) :) [concat(ArgumentList/*[3]/@Name, '') = ancestor::MethodDeclaration/FormalParameters/FormalParameter[3]/VariableId/@Name or ancestor::MethodDeclaration/FormalParameters[@Size <= 2 or FormalParameter[3][( (: or its classType is equal to the local var classtype :) (ClassType/@SimpleName|PrimitiveType/@Kind) = (ancestor::MethodDeclaration//LocalVariableDeclaration|ancestor::ClassBody//FieldDeclaration)[ (: which name equals to the name of the 3rd argument of the method call :) .//VariableId/@Name = ancestor::MethodDeclaration//MethodCall[@MethodName = ancestor::MethodDeclaration/@Name]/ArgumentList/VariableAccess[3]/@Name][ (: and which classType is equal to the classtype of the 3rd parameter :) ((ClassType/@SimpleName|PrimitiveType/@Kind) = ancestor::MethodDeclaration/FormalParameters/FormalParameter[3]/(ClassType/@SimpleName|PrimitiveType/@Kind)) ]/(ClassType/@SimpleName|PrimitiveType/@Kind) or (: or its classType is equal to the field classtype :) (ClassType/@SimpleName|PrimitiveType/@Kind) = ancestor::ClassBody//FieldDeclaration[ (: which name equals to the name of the 3rd argument of the method call :) .//VariableId/@Name = ..//MethodDeclaration//MethodCall[@MethodName = ancestor::MethodDeclaration/@Name]/ArgumentList/VariableAccess[3]/@Name ][ (: and which classType is equal to the classtype of the 3rd parameter :) ((ClassType/@SimpleName|PrimitiveType/@Kind) = ..//MethodDeclaration/FormalParameters/FormalParameter[3]/(ClassType/@SimpleName|PrimitiveType/@Kind)) ]/(ClassType/@SimpleName|PrimitiveType/@Kind)) ]]] ]]></value> </property> <property name="tags" value="cpu,jpinpoint-rule,memory,performance,pitfall,sustainability-medium" type="String" description="classification"/> </properties> <example> <![CDATA[ class InfiniteRecursionBad { private void foo() { boolean success = tryRemoteCall(); if (!success) { delay(10, MILLISECONDS); foo(); // bad } } } class FiniteRecursionGood { private static final int MAX_ATTEMPTS = 5; private void foo(int attemptsLeft) { boolean success = tryRemoteCall(); if (!success && attemptsLeft > 0 ) { delay(10, MILLISECONDS); foo(--attemptsLeft); } } } class IterationsBetter { private static final int MAX_ATTEMPTS = 5; private void foo() { int attempt = 0; boolean success = false; while (!success && attempt++ < MAX_ATTEMPTS) { success = tryRemoteCall(); if (!success) { delay(10, MILLISECONDS); } } } } ]]> </example> </rule> <rule name="AvoidLoadingAllFromFile" language="java" message="Files.readAll methods load all bytes from a file into memory: a risk of memory problems." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isio03"> <description>Problem: Files.readAllBytes and Files.readAllLines load all bytes from a file into the heap memory. This may result in an OutOfMemoryError crash, or long gc pauses and slow responses. Solution: Stream-through: use streaming all the way, don't store the whole thing in memory, don't use byte arrays. Often, functionality can be achieved in a streaming way. Note: not a problem for small files. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,memory,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration//MethodCall[pmd-java:matchesSig('java.nio.file.Files#readAllBytes(java.nio.file.Path)') or pmd-java:matchesSig('java.nio.file.Files#readAllLines(java.nio.file.Path)') or pmd-java:matchesSig('java.nio.file.Files#readAllLines(java.nio.file.Path,_)') ] ]]></value> </property> </properties> <example> <![CDATA[ class Bad { void bad(Path path) { byte[] fileBytes = Files.readAllBytes(path); // bad List<String> fileLines = Files.readAllLines(path); // bad // process bytes / lines } } class Good { void good(Path in) throws IOException { try (BufferedReader reader = Files.newBufferedReader(in)) { String line = reader.readLine(); // process line by line } } } ]]> </example> </rule> <rule name="AvoidLombokAnnotationForNonExistentFields" language="java" message="Avoid Lombok annotations for fields of a class while the class has no fields." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl=""> <description>Problem: Lombok annotations for fields [@Getter, @Setter, @EqualsAndHashCode, @Value, @Data] are of no use and confusing in case the class has no fields. Solution: Remove the Lombok annotation. (jpinpoint-rules)</description> <priority>4</priority> <properties> <property name="tags" value="jpinpoint-rule,confusing" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassDeclaration/ModifierList/Annotation [pmd-java:typeIs('lombok.Getter') or pmd-java:typeIs('lombok.Setter') or pmd-java:typeIs('lombok.Value') or pmd-java:typeIs('lombok.Data') or pmd-java:typeIs('lombok.EqualsAndHashCode')] [ancestor::ClassDeclaration/ClassBody[count(FieldDeclaration) = 0]] ]]></value> </property> </properties> <example> <![CDATA[ @Data // bad public class BusinessException extends RuntimeException { // no fields, no methods } ]]> </example> </rule> <rule name="AvoidMultipleConcatStatements" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Multiple statements concatenate to the same String. Use StringBuilder append." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isu02"> <description>Multiple statements concatenate to the same String. Problem: Each statement with one or more +-operators creates a hidden temporary StringBuilder, a char[] and a new String object, which all have to be garbage collected. Solution: Use StringBuilder.append. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: x += y with only one preceding x += v or x = x + w :) //AssignmentExpression[@Operator='+='][VariableAccess/@Name=ancestor::Block//VariableDeclarator/VariableId[pmd-java:typeIs('java.lang.String')]/@Name] [(VariableAccess/@Name = ../preceding-sibling::ExpressionStatement/AssignmentExpression[@Operator='+=']/VariableAccess/@Name or VariableAccess/@Name = ../preceding-sibling::ExpressionStatement//InfixExpression[@Operator='+']/VariableAccess/@Name) and not ( VariableAccess/@Name = ../preceding-sibling::ExpressionStatement[2]/AssignmentExpression[@Operator='+=']/VariableAccess/@Name or VariableAccess/@Name = ../preceding-sibling::ExpressionStatement[2]//InfixExpression[@Operator='+']/VariableAccess/@Name ) ] | (: x = x + y with only one preceding x += v or x = x + w :) //InfixExpression[@Operator='+'][VariableAccess/@Name=ancestor::AssignmentExpression/VariableAccess/@Name][VariableAccess/@Name=ancestor::Block//VariableDeclarator/VariableId[pmd-java:typeIs('java.lang.String')]/@Name] [(VariableAccess/@Name = ancestor::ExpressionStatement/preceding-sibling::ExpressionStatement//InfixExpression[@Operator='+']/VariableAccess/@Name or VariableAccess/@Name = ancestor::ExpressionStatement/preceding-sibling::ExpressionStatement/AssignmentExpression[@Operator='+=']/VariableAccess/@Name) and not ( VariableAccess/@Name = ancestor::ExpressionStatement/preceding-sibling::ExpressionStatement[2]/AssignmentExpression[@Operator='+=']/VariableAccess/@Name or VariableAccess/@Name = ancestor::ExpressionStatement/preceding-sibling::ExpressionStatement[2]//InfixExpression[@Operator='+']/VariableAccess/@Name ) ]/.. ]]></value> </property> </properties> <example> <![CDATA[ class ConcatsExample { private String[] values = {"tic", "tac"}; String bad() { String logStatement = ""; logStatement += values[0]; return logStatement += values[1]; // bad } String good() { StringBuilder logStmtBuilder = new StringBuilder(); return logStmtBuilder.append(values[0]).append(values[1]).toString(); // good } } ]]> </example> </rule> <rule name="AvoidMutableLists" language="java" message="List is not modified after object construction, make it immutable or unmodifiable" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#pml01"> <description>Problem: A list which is unnecessarily mutable may accidentally be added to and cause a memory leak. Solution: For the list which is not actually modified, make it impossible to modify after object construction/initialization: use Java 9 List.of, Java 11 List.copyOf, Collections.unmodifiableList or Guava ImmutableList. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="jpinpoint-rule,pitfall" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassDeclaration[not(ModifierList/Annotation [pmd-java:typeIs('lombok.Getter') or pmd-java:typeIs('lombok.Setter') or pmd-java:typeIs('lombok.Value') or pmd-java:typeIs('lombok.Data')])] (: a field of type List :) //FieldDeclaration[//VariableId[pmd-java:typeIs('java.util.List')]] (: not for field with @Getter and @Setter, it is accessed outside of the class :) [not(ModifierList/Annotation[pmd-java:typeIs('lombok.Getter') or pmd-java:typeIs('lombok.Setter')])] (: exclude ConfigurationProperties classes :) [count(ancestor::ClassDeclaration/ModifierList/Annotation[@SimpleName='ConfigurationProperties']) = 0] (: exclude when JPA annotated :) [count(ModifierList/Annotation[@SimpleName=('OneToMany', 'ManyToMany')]) = 0] //VariableId[ (: with no method defined at that level that uses the field with a modify operation (case field.modOp) :) not(@Name = ancestor::ClassBody/MethodDeclaration/Block//MethodCall[ starts-with(@MethodName, 'add') or starts-with(@MethodName, 'remove') or @MethodName = ('set', 'clear', 'replaceAll', 'retainAll') ]/(VariableAccess|FieldAccess)/@Name) ] [ (: field is initialized with List constructor :) ../ConstructorCall[pmd-java:typeIs('java.util.List')] or (: or static block / constructor has field initialized with List allocation :) ancestor::ClassBody/(Initializer|ConstructorDeclaration)//AssignmentExpression[ConstructorCall[pmd-java:typeIs('java.util.List')]]/(VariableAccess|FieldAccess)[1]/@Name = @Name or (: or constructor has a simple assignment to the field, param or literal: no arguments :) ancestor::ClassBody/ConstructorDeclaration/Block//AssignmentExpression[(VariableAccess|FieldAccess)[2][pmd-java:typeIs('java.util.List')]]/(VariableAccess|FieldAccess)[1]/@Name = @Name ] ]]></value> </property> </properties> <example> <![CDATA[ public class Bad { List unmutated = new ArrayList(Arrays.asList("one", "two")); // bad List mutated = new ArrayList(); private void mutate() { mutated.add("one"); } } public class Good { List unmutated1 = Collections.unmodifiableList(Arrays.asList("one", "two")); // Java 8 List unmutated2 = List.of("one", "two"); // Java 9+ List mutated = new ArrayList(); private void mutate() { mutated.add("one"); } } ]]> </example> </rule> <rule name="AvoidReStreamingEnumValues" language="java" message="Avoid re-streaming enum values to find a value by a field." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuoc07"> <description>Problem: the time to find element is O(n); n = the number of enum values. This identical processing is executed for every call. Considered problematic when n > 3. Solution: use a static field-to-enum-value Map. Access time is O(1), provided the hashCode is well-defined. For one String field, usually toString returns that field. Implement a fromString method to provide the reverse conversion by using the map. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //EnumDeclaration/EnumBody[count(EnumConstant) > 3]//MethodDeclaration/Block //MethodCall[pmd-java:matchesSig('java.util.stream.Stream#findFirst()') or pmd-java:matchesSig('java.util.stream.Stream#findAny()')] [//MethodCall[pmd-java:matchesSig('java.util.stream.Stream#of(_)') or pmd-java:matchesSig('java.util.Arrays#stream(_)')] [ArgumentList/MethodCall[pmd-java:matchesSig('_#values()')]]] ]]></value> </property> </properties> <example> <![CDATA[ // BAD public enum Fruit { APPLE("apple"), ORANGE("orange"), BANANA("banana"), KIWI("kiwi"); private final String name; Fruit(String name) { this.name = name; } @Override public String toString() { return name; } public static Optional<Fruit> fromString(String name) { return Stream.of(values()).filter(v -> v.toString().equals(name)).findAny(); // bad: iterates for every call, O(n) access time } } Usage: `Fruit f = Fruit.fromString("banana");` // GOOD public enum Fruit { APPLE("apple"), ORANGE("orange"), BANANA("banana"), KIWI("kiwi"); private static final Map<String, Fruit> nameToValue = Stream.of(values()).collect(toMap(Object::toString, v -> v)); private final String name; Fruit(String name) { this.name = name; } @Override public String toString() { return name; } public static Optional<Fruit> fromString(String name) { return Optional.ofNullable(nameToValue.get(name)); // good, get from Map, O(1) access time } } ]]> </example> </rule> <rule name="AvoidRecompilingPatterns" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Pattern.compile is used in a method. Compiling a regex pattern can be expensive, make it a static final field." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ireu02"> <description>A regular expression is compiled on every invocation. Problem: this can be expensive, depending on the length of the regular expression. Solution: Usually a pattern is a literal, not dynamic and can be compiled only once. Assign it to a private static field. java.util.Pattern objects are thread-safe so they can be shared among threads. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration//MethodCall[pmd-java:matchesSig('java.util.regex.Pattern#compile(java.lang.String)') and not(.//VariableAccess[@Name = ancestor::MethodDeclaration//FormalParameter/VariableId/@Name])] ]]></value> </property> </properties> <example> <![CDATA[ class Bad { public static final String STR_PAT1 = "[A-Z][a-z]+"; public static void bad() { Pattern p1 = Pattern.compile(STR_PAT1); // bad Pattern p2 = Pattern.compile("(?=\\p{Lu})"); // bad boolean b = p1.matcher("Start ").matches(); } } class Good { public static final Pattern PAT1 = Pattern.compile("[A-Z][a-z]+"); public static final Pattern PAT2 = Pattern.compile("(?=\\p{Lu})"); public static void good() { boolean b = PAT1.matcher("Start ").matches(); } } ]]> </example> </rule> <rule name="AvoidRecompilingXPathExpression" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="XPathExpression is created and compiled every time. Beware it is thread-unsafe." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ux02"> <description>XPathExpression is created and compiled on every method call, compiled possibly implicitly by XPath::evaluate. Problem: Creation of XPath and compilation of XPathExpression takes time. It may slow down your application. Solution: 1. Avoid XPath usage. 2. Compile the xpath expression as String into a XPathExpression. However, since XPath and XPathExpression classes are thread-unsafe, they are not easily cached. Caching it in a ThreadLocal may be a solution. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration//MethodCall[pmd-java:matchesSig('javax.xml.xpath.XPath#compile(java.lang.String)') or pmd-java:matchesSig('javax.xml.xpath.XPath#evaluate(java.lang.String,java.lang.Object,javax.xml.namespace.QName)')] ]]></value> </property> </properties> <example> <![CDATA[ class Bad { public static NodeList bad1(Document doc) { XPath xpath = XPathFactory.newInstance().newXPath(); XPathExpression expr = xpath.compile("//book[author='Isaac Asimov']/title/text()"); // bad return (NodeList) expr.evaluate(doc, XPathConstants.NODESET); } public static NodeList bad2(Document doc) throws XPathExpressionException { XPath xpath = XPathFactory.newInstance().newXPath(); String xPathQuery = "//book[author='Isaac Asimov']/title/text()"; return (NodeList) xpath.evaluate(xPathQuery, doc, XPathConstants.NODESET); // bad } } class Good { private static final ThreadLocal<XPathFactory> tlFac = ThreadLocal.withInitial(XPathFactory::newInstance); private static final ThreadLocal<XPathExpression> tlExpr; static { XPath xpath = tlFac.get().newXPath(); try { XPathExpression expr = xpath.compile("//book[author='Isaac Asimov']/title/text()"); tlExpr = ThreadLocal.withInitial(() -> expr); // good } catch (XPathExpressionException e) { throw new RuntimeException(e); } } public static NodeList good(Document doc) throws XPathExpressionException { return (NodeList) tlExpr.get().evaluate(doc, XPathConstants.NODESET); // good } } ]]> </example> </rule> <rule name="AvoidRecreatingDateTimeFormatter" message="Avoid recreating DateTimeFormatter, it is relatively expensive." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#idtf02"> <description> Problem: Recreating a DateTimeFormatter is relatively expensive. Solution: Java 8+ java.time.DateTimeFormatter is thread-safe and can be shared among threads. Create the formatter from a pattern only once, to initialize a static final field. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //FieldDeclaration [ClassType[pmd-java:typeIs('java.time.format.DateTimeFormatter') or pmd-java:typeIs('org.joda.time.format.DateTimeFormatter')]] [(not(pmd-java:modifiers() = 'static') and VariableDeclarator[@Initializer=true()]) or not(pmd-java:modifiers() = 'final')] | //MethodDeclaration//ConstructorCall[pmd-java:matchesSig('org.joda.time.format.DateTimeFormatter#new(_,_)')] | //(MethodDeclaration|ConstructorDeclaration)//MethodCall[((pmd-java:matchesSig('java.time.format.DateTimeFormatter#ofPattern(_)') or pmd-java:matchesSig('org.joda.time.format.DateTimeFormat#forPattern(_)')) and not(ArgumentList/VariableAccess/@Name = ancestor::Block/..//FormalParameter/VariableId/@Name)) or pmd-java:matchesSig('java.time.format.DateTimeFormatterBuilder#toFormatter()') or pmd-java:matchesSig('java.time.format.DateTimeFormatterBuilder#toFormatter(_)') or pmd-java:matchesSig('org.joda.time.format.DateTimeFormatterBuilder#toFormatter()') or pmd-java:matchesSig('org.joda.time.format.ISODateTimeFormat#_()') or pmd-java:matchesSig('org.joda.time.format.DateTimeFormat#fullDateTime()') ] ]]> </value> </property> </properties> </rule> <rule name="AvoidRecreatingSecurityProviders" language="java" message="Avoid re-creating security providers, this is expensive." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuosf01"> <description>Problem: Creating a security provider is expensive because of loading of algorithms and other classes. Additionally, it uses synchronized which leads to lock contention when used with multiple threads. Solution: This only needs to happen once in the JVM lifetime, because once loaded, the provider is typically available from the Security class. Create the security provider only once: only in case it is nog available from the Security class, yet. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,io,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration [not((@Name='main' and @Static=true())or ModifierList/Annotation/@SimpleName='PostConstruct' or .//IfStatement//InfixExpression [@Operator='=='][VariableAccess[pmd-java:typeIs('java.security.Provider')] and NullLiteral] )] //ConstructorCall[pmd-java:typeIs('java.security.Provider') or pmd-java:typeIs('org.bouncycastle.jce.provider.BouncyCastleProvider')] ]]></value> </property> </properties> <example> <![CDATA[ import java.security.*; import javax.crypto.*; import org.bouncycastle.jce.provider.*; class Foo { public Cipher initBlowfishBad() throws GeneralSecurityException { Security.addProvider(new BouncyCastleProvider()); // bad // build a Cipher } public Cipher initBlowfishGood() throws GeneralSecurityException { Provider bouncyCastleProvider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); if (bouncyCastleProvider == null) { bouncyCastleProvider = new BouncyCastleProvider(); Security.addProvider(bouncyCastleProvider); } // build a Cipher } } ]]> </example> </rule> <rule name="AvoidReflectionInToStringAndHashCode" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Reflection is used in toString or hashCode, which is expensive." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#uuor01"> <description>Problem: Reflection is relatively expensive. Solution: Avoid to use reflection. Use the non-reflective, explicit way, like generation by IDE or Lombok. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[starts-with(@MethodName, 'reflection')][(TypeExpression|ConstructorCall) [pmd-java:typeIs('org.apache.commons.lang3.builder.EqualsBuilder') or pmd-java:typeIs('org.apache.commons.lang3.builder.HashCodeBuilder')]] ]]> </value> </property> </properties> <example> <![CDATA[ class Bad { private int state; public boolean equals(Object o) { return EqualsBuilder.reflectionEquals(o, this); // bad } } class Good { private int state; public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Good)) return false; return new EqualsBuilder().append(((Good)o).state, state).isEquals(); } } ]]> </example> </rule> <rule name="AvoidSimpleDateFormat" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="SimpleDateFormat is used. Since it is thread-unsafe, it needs expensive recreation." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#idtf01"> <description>Problem: java.util.SimpleDateFormat is thread-unsafe. The usual solution is to create a new one when needed in a method. Creating SimpleDateFormat is relatively expensive. Solution: Use java.time.DateTimeFormatter. These classes are immutable, thus thread-safe and can be made static. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ConstructorCall/ClassType[pmd-java:typeIs('java.text.SimpleDateFormat') and not ((ancestor::LocalVariableDeclaration/following-sibling::ExpressionStatement/MethodCall|ancestor::MethodCall) [@MethodName='setDateFormat']/VariableAccess[pmd-java:typeIs('com.fasterxml.jackson.databind.ObjectMapper') or pmd-java:typeIs('com.fasterxml.jackson.dataformat.xml.XmlMapper')]) ] ]]></value> </property> </properties> <example> <![CDATA[ public class Foo { private String toKeyBad(final Date date) { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); //bad return formatter.format(date); } private String toKeyGood(final LocalDate localDate) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); //good return formatter.format(localDate); } } ]]> </example> </rule> <rule name="AvoidStringBuffer" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="StringBuffer is used. It introduces locking overhead, use StringBuilder." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isu01" > <description>Problem: StringBuffer introduces locking overhead because it is thread safe. Its thread-safety is rarely needed. Solution: Replace StringBuffer by StringBuilder. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ConstructorCall/ClassType[pmd-java:typeIs('java.lang.StringBuffer')] ]]> </value> </property> </properties> <example> class Foo { public void bad() { var sb = new StringBuffer(); // bad } public void good() { var sb = new StringBuilder(); // good } } </example> </rule> <rule name="AvoidTimeUnitConfusion" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="A primitive variable identifier or @Value member ends with time, timeout, duration or similar: time unit is missing." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi19"> <description>Problem: Time unit like hours, seconds, milliseconds is not specified and may be assumed differently by readers. Different assumptions will lead to errors or hidden problems like ineffective caches. Solution: Specify the time unit in the identifier, like connectTimeoutMillis. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="confusing,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: for fields, formal parameters and local variables :) //(VariableDeclarator[../PrimitiveType]|FormalParameter[PrimitiveType])/VariableId[ ends-with(lower-case(@Name), 'timetolive') or ends-with(lower-case(@Name), 'time_to_live') or ends-with(lower-case(@Name), 'time') or ends-with(lower-case(@Name), 'timeout') or ends-with(lower-case(@Name), 'time_out') or ends-with(lower-case(@Name), 'duration') or ends-with(lower-case(@Name), 'durationout') or ends-with(lower-case(@Name), 'duration_out') or ends-with(lower-case(@Name), 'ttl') or ends-with(lower-case(@Name), 'timestamp')] , (: @Value :) //Annotation[@SimpleName='Value' and .//MemberValuePair[@Image='value']/StringLiteral[ ends-with(lower-case(@Image), 'timetolive') or ends-with(lower-case(@Image), 'time_to_live}"') or ends-with(lower-case(@Image), 'time}"') or ends-with(lower-case(@Image), 'timeout}"') or ends-with(lower-case(@Image), 'time_out}"') or ends-with(lower-case(@Image), 'duration}"') or ends-with(lower-case(@Image), 'durationout}"') or ends-with(lower-case(@Image), 'duration_out}"') or ends-with(lower-case(@Image), 'ttl}"') or ends-with(lower-case(@Image), 'timestamp}"')]] ]]> </value> </property> </properties> <example> <![CDATA[ @Autowired public RetrieveCache(final @Value("${cache.expiryTime}") long timeToLive) { // 2x bad } @Autowired public RetrieveCache(final @Value("${cache.expiryTimeMillis}") long timeToLiveMillis) { // 2x good } ]]> </example> </rule> <rule name="AvoidUnconditionalBuiltLogStrings" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Log String is built irrespective of log level." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#il02"> <description>A String to be logged is built unconditionally. Problem: String building, concatenation and/or other operations happen before the debug, trace or info method executes, so independent of the need to actually log. Concatenation is relatively expensive. Solution: Build the String conditionally on the log level, within an if statement. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall [pmd-java:matchesSig('java.lang.StringBuilder#append(java.lang.String)')] (: variable in append following used in logging :) [VariableAccess/@Name=(ancestor::ExpressionStatement|ancestor::ForeachStatement|ancestor::DoStatement|ancestor::WhileStatement) /(following-sibling::TryStatement|following-sibling::ExpressionStatement)//MethodCall [@MethodName=('trace', 'debug', 'info')] [ (: supported logger libs :) (VariableAccess[ pmd-java:typeIs('org.slf4j.Logger') or pmd-java:typeIs('java.util.logging.Logger') or pmd-java:typeIs('org.apache.commons.logging.Log') ]) or (: Slfj4 annotation makes the log variable AmbiguousName as there is no explicit log field :) (AmbiguousName[ancestor::ClassDeclaration[pmd-java:hasAnnotation('lombok.extern.slf4j.Slf4j')]]) ] //VariableAccess/@Name ] (: variable in append following, not used in anything other than logging :) [not( VariableAccess/@Name=(ancestor::ExpressionStatement|ancestor::ForeachStatement|ancestor::DoStatement|ancestor::WhileStatement) /(following-sibling::TryStatement|following-sibling::ExpressionStatement|following-sibling::ReturnStatement)//MethodCall [not(@MethodName=('trace', 'debug', 'info')) or VariableAccess[not(pmd-java:typeIs('org.slf4j.Logger')) and not(pmd-java:typeIs('java.util.logging.Logger')) and not(pmd-java:typeIs('org.apache.commons.logging.Log'))]] //VariableAccess/@Name )] (: only if unconditional, and not || before the log check in the if statement :) [not(ancestor::IfStatement//MethodCall[ @MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable') and count(../../InfixExpression[@Operator='||' or @Operator='|' or @Operator='^']) = 0 ]) ] (: except if method name starts with 'log', and it is non-public, .. :) [not(ancestor::MethodDeclaration [starts-with(@Name, 'log') and @Visibility = 'private'] (: and the method is called only conditionally: it is called inside an if :) [@Name=ancestor::ClassBody//MethodCall[ancestor::IfStatement/MethodCall[@MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable')]]/@MethodName] (: and it is not called not inside an if :) [not(@Name=ancestor::ClassBody//MethodCall[not(ancestor::IfStatement/MethodCall[@MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable')])]/@MethodName)] )] ]]></value> </property> </properties> <example> <![CDATA[ class Foo { public void bad(List values) { var logLine = new StringBuilder(); for (String val : values) { logLine.append(val + ", "); // bad } LOG.debug("log: {}.", logLine); } public void good(List values) { if (LOG.isDebugEnabled()) { var logLine = new StringBuilder(); for (String val : values) { logLine.append(val + ", "); } LOG.debug("log: {}.", logLine); } } } ]]> </example> </rule> <rule name="AvoidUnnecessaryStringBuilderCreation" language="java" message="Use concatenation (+) instead of new StringBuilder if only one statement." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isu05"> <description>Problem: Creating a StringBuilder and using append is more verbose, less readable and less maintainable than simply using String concatenation (+). For one statement resulting in a String, creating a StringBuilder and using append is not faster than simply using concatenation. Solution: Simply concatenate Strings in one statement, it is more concise, better readable and more maintainable. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="bad-practice,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: append used in one chained statement on constructor of StringBuilder and with chained toString :) //MethodDeclaration//Block//MethodCall[@MethodName = "toString"]/MethodCall[@MethodName='append'] [.//ConstructorCall[pmd-java:typeIs('java.lang.AbstractStringBuilder')]] | (: or local declared builder var with an append :) //MethodDeclaration//Block/LocalVariableDeclaration[ VariableDeclarator[ VariableId/@Name=ancestor::Block[1]//MethodCall[@MethodName='append']/VariableAccess/@Name ] /ConstructorCall/ClassType[pmd-java:typeIs('java.lang.AbstractStringBuilder')] ] [ (: and toString is called (possibly chained) on the var in same block, with no other operations than append :) ..//MethodCall[@MethodName='append']/VariableAccess/@Name=..//(ReturnStatement|ExpressionStatement|LocalVariableDeclaration)//MethodCall[@MethodName='toString'] [not(..//MethodCall[@MethodName!=('append') and @MethodName!=('toString')])]//VariableAccess/@Name ] [ not( ..//MethodCall[@MethodName='append']/VariableAccess[ (: and not append on var used in inner block like a ForStatement or IfStatement :) @Name = ancestor::Block//Block//MethodCall[@MethodName='append']/VariableAccess/@Name (: and not append in lambda expression, issue 195 :) or ancestor::LambdaExpression (: and var not passed as argument to method :) or (@Name = ancestor::MethodDeclaration//MethodCall/ArgumentList/VariableAccess/@Name) ] ) ] ]]></value> </property> </properties> <example> <![CDATA[ class Foo { String name; String value; public String bad() { return new StringBuilder() .append(name) // bad .append(" = ")// bad .append(value)// bad .toString(); } public String good() { return name + " = " + value; } } ]]> </example> </rule> <rule name="AvoidWideScopeXPathExpression" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="XPathExpression targets a wide scope, this is potentially slow." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ux01"> <description>The XPathExpression targets a wide scope since it starts with '//'. Problem: XPath has to search in a wide scope for occurrences, this may take a while. Solution: 1. Avoid XPath usage. 2. Make the scope as narrow as possible, do not start with '//'. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[pmd-java:matchesSig('javax.xml.xpath.XPath#compile(java.lang.String)')]/ArgumentList/StringLiteral[starts-with(@Image, '"//')] ]]></value> </property> </properties> <example> <![CDATA[ public class Foo { public bad() throws XPathExpressionException { XPath xpath = XPathFactory.newInstance().newXPath(); XPathExpression expr = xpath.compile("//book[author='Isaac Asimov']/title/text()"); // bad } public good() throws XPathExpressionException { XPath xpath = XPathFactory.newInstance().newXPath(); XPathExpression expr = xpath.compile("/public/library/books/book[author='Isaac Asimov']/title/text()"); } } ]]> </example> </rule> <rule name="AvoidXPathAPIUsage" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="XPathAPI is used. XPathAPI implementation has bad performance." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ux03"> <description>XPathAPI is used. Problem: XPathAPI implementation is slow. Solution: 1. try to avoid using XPathAPI. 2. improve performance by using jvm parameters and possibly CachedXPathAPI. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall/TypeExpression/ClassType[pmd-java:typeIs('org.apache.xpath.XPathAPI')] ]]></value> </property> </properties> </rule> <rule name="AvoidXPathUsage" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="XPath is used. XPath implementation has bad performance." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ux03"> <description>XPath is used. Problem: XPath implementation is slow. Solution: 1. avoid using XPath. 2. improve performance by using jvm parameters and possibly Cached XPath API. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[pmd-java:matchesSig('javax.xml.xpath.XPathFactory#newInstance()')] [not (ancestor::FieldDeclaration/ClassType[pmd-java:typeIs('java.lang.ThreadLocal')])] ]]></value> </property> </properties> </rule> <rule name="BufferFileStreaming" language="java" message="FileInputStream and FileOutputStream are not buffered, add buffering." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isio05"> <description>Problem: With FileInputStream and FileOutputStream, file access is not buffered. The stream is read-from/written-to file byte by byte, where each operating system call has its overhead, which makes it slow. Solution: Use buffering to read/write a chunk of bytes at once with much lower overhead. Use BufferedOutput/InputStream which has a buffer size of 8 kB by default to write at once. Make sure to close it after use. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,io,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //Resource//ConstructorCall[pmd-java:typeIs('java.io.FileInputStream') or pmd-java:typeIs('java.io.FileOutputStream')] [not (ancestor::TryStatement//ConstructorCall[pmd-java:typeIs('java.io.BufferedInputStream') or pmd-java:typeIs('java.io.BufferedOutputStream')])] ]]></value> </property> </properties> <example> <![CDATA[ import java.io.*; class BufferFileStreaming { void bad(String inputFilename, String outputFilename) { try (FileInputStream fis = new FileInputStream(inputFilename)) { // bad //use fis } } void good(String inputFilename, String outputFilename) { try (InputStream bfis = new BufferedInputStream(new FileInputStream(inputFilename))) { // good //use bfis } } } ]]> </example> </rule> <rule name="BufferFilesNewStream" language="java" message="Files.newInputStream or Files.newOutputStream is not buffered, add buffering." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isio06"> <description>Problem: Files.newInputStream or Files.newOutputStream is not buffered. The stream is read from/written to file byte by byte, where each operating system call has its overhead which makes it slow. Solution: Use buffering to read/write a chunk of bytes at once with much lower overhead. Use e.g. BufferedInputStream or BufferedOutputStream which has a buffer size of 8 kB to read/write at once. Make sure to close it after use. Note that IOUtils methods take care of buffering the inputStream. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,io,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[TypeExpression[pmd-java:typeIs('java.nio.file.Files')]] (: the following methods use a buffer with File[Input/Output]Stream read/write method implementation of [Input/Output]Stream :) [not(ancestor::MethodDeclaration//MethodCall[pmd-java:matchesSig('java.io.InputStream#transferTo(java.io.OutputStream)') or pmd-java:matchesSig('java.io.InputStream#readAllBytes()') or pmd-java:matchesSig('java.io.InputStream#readNBytes(int)') or pmd-java:matchesSig('java.io.InputStream#read(byte[])') or pmd-java:matchesSig('java.io.InputStream#read(byte[],int,int)') or pmd-java:matchesSig('java.nio.file.Files#copy(_*)') or pmd-java:matchesSig('org.apache.commons.io.IOUtils#copy(_*)')])] [(@MethodName='newInputStream' and (not(ancestor::MethodDeclaration//ConstructorCall[pmd-java:typeIs('java.io.BufferedInputStream') or pmd-java:typeIs('java.io.BufferedReader')])) and not(ancestor::MethodDeclaration//MethodCall[starts-with(@MethodName, 'read') and TypeExpression[pmd-java:typeIs('org.apache.commons.io.IOUtils')]]) or (@MethodName='newOutputStream' and not(ancestor::MethodDeclaration//ConstructorCall[pmd-java:typeIs('java.io.BufferedOutputStream') or pmd-java:typeIs('java.io.BufferedWriter')]) ))] ]]></value> </property> </properties> <example> <![CDATA[ class Foo { OutputStream badO(String path) throws IOException { return java.nio.file.Files.newOutputStream(Paths.get(path)); // bad } OutputStream goodO(String path) throws IOException { return new BufferedOutputStream(java.nio.file.Files.newOutputStream(Paths.get(path))); } } ]]> </example> </rule> <rule name="EqualsOperationInconsistentWithHashCode" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Equals uses a conversion inconsistent with hashCode." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#ieah03"> <description>Problem: Equal objects may have different hashCodes and end-up in different buckets of a Map/Set. Strange things can happen like adding an object to a Set and not being able to find it back. Solution: When objects are equal, hashCode needs to be equal, too. Use the same fields in equals and hashCode and use identical conversions like toUpperCase() in both when needed. Don't use equalsIgnoreCase. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="correctness,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: the number of toLowerCase on a field used in equals should not be larger than that of hashCode :) //FieldDeclaration//VariableId[ (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='equals' and (FormalParameters/@Size = 1)]/Block//MethodCall[pmd-java:matchesSig('java.lang.String#toLowerCase()')]//VariableAccess/@Name) > (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='hashCode' and (FormalParameters/@Size = 0)]/Block//MethodCall[pmd-java:matchesSig('java.lang.String#toLowerCase()')]//VariableAccess/@Name) ] (: go to violating toLowerCase in equals :) /ancestor::ClassBody[1]//MethodDeclaration[@Name='equals']/Block//MethodCall[pmd-java:matchesSig('java.lang.String#toLowerCase()')] , (: the number of toUpperCase on a field used in equals should not be larger than that of hashCode :) //FieldDeclaration//VariableId[ (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='equals' and (FormalParameters/@Size = 1)]/Block//MethodCall[pmd-java:matchesSig('java.lang.String#toUpperCase()')]//VariableAccess/@Name) > (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='hashCode' and (FormalParameters/@Size = 0)]/Block//MethodCall[pmd-java:matchesSig('java.lang.String#toUpperCase()')]//VariableAccess/@Name) ] (: go to violating toUpperCase in equals :) /ancestor::ClassBody[1]//MethodDeclaration[@Name='equals']/Block//MethodCall[pmd-java:matchesSig('java.lang.String#toUpperCase()')] , (: equalsIgnoreCase on field, in equals, not allowed :) //MethodDeclaration[@Name='equals' and (FormalParameters/@Size = 1)]/Block//MethodCall[pmd-java:matchesSig('java.lang.String#equalsIgnoreCase(java.lang.String)')] ]]> </value> </property> </properties> <example> <![CDATA[ class Bad1 { String field1; String field2; public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Bad1 that = (Bad1) o; if (field1 != null ? !field1.equals(that.field1) : that.field1 != null) return false; return field2 != null ? field2.equalsIgnoreCase(that.field2) : that.field2 == null; // ignore case, bad } public int hashCode() { return Objects.hash(field1, field2); } } class Bad2 { String field1; String field2; public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Bad2 that = (Bad2) o; return Objects.equals(field1.toUpperCase(), that.field1.toUpperCase()) && // bad Objects.equals((field2.toLowerCase()), that.field2.toLowerCase()); // bad } public int hashCode() { return Objects.hash(field1, field2); } } class Bad3 { String field1; String field2; public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Bad3 that = (Bad3) o; return Objects.equals(field1.toUpperCase(), that.field1.toUpperCase()) && // bad Objects.equals((field2.toLowerCase()), that.field2.toLowerCase()); } public int hashCode() { return Objects.hash(field1.toLowerCase(), field2.toLowerCase()); } } class Good { String field1, field2; public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Good that = (Good) o; return Objects.equals(field1, that.field1) && Objects.equals(field2.toUpperCase(), that.field2.toUpperCase()); // ignore case same way } public int hashCode() { return Objects.hash(field1, field2.toUpperCase()); // ignore case same way } } ]]> </example> </rule> <rule name="FieldOfHashCodeMissingInEquals" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Equals does not use all fields used by hashCode." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#ieah03"> <description>Problem: Equal objects may have different hashCodes and end-up in different buckets of a Map/Set. Strange things can happen like adding an object to a Set and not being able to find it back. Solution: When objects are equal, hashCode needs to be equal, too. Use the same fields in equals and hashCode. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="correctness,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //FieldDeclaration/VariableDeclarator[ ((@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='equals' and FormalParameters/@Size = 1]/Block//(FieldAccess|VariableAccess)/@Name) < (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='hashCode' and FormalParameters/@Size = 0]/Block//(FieldAccess|VariableAccess)/@Name) (: and the field is not used to assign a value to in the hashCode(), typically a hash variable to cache the calculated hashcode :) and not(@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='hashCode' and FormalParameters/@Size = 0]/Block//AssignmentExpression[@Operator='=']/(FieldAccess|VariableAccess)/@Name)) ] | //FieldDeclaration/VariableDeclarator[ (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='equals' and FormalParameters/@Size = 1]/Block//MethodCall[@MethodName='toLowerCase']/(FieldAccess|VariableAccess)/@Name) > (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='hashCode' and FormalParameters/@Size = 0]/Block//MethodCall[@MethodName='toLowerCase']/(FieldAccess|VariableAccess)/@Name) ] | //FieldDeclaration/VariableDeclarator[ (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='equals' and FormalParameters/@Size = 1]/Block//MethodCall[@MethodName='toUpperCase']/(FieldAccess|VariableAccess)/@Name) > (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='hashCode' and FormalParameters/@Size = 0]/Block//MethodCall[@MethodName='toUpperCase']/(FieldAccess|VariableAccess)/@Name) ] ]]> </value> </property> </properties> <example> <![CDATA[ class Good1 { String field1, field2; public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Good1 that = (Good1) o; return Objects.equals(field1, that.field1) && Objects.equals(field2, that.field2); } public int hashCode() { return Objects.hash(field1, field2); } } class Bad { String field1; String field2; //bad public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Bad that = (Bad) o; return Objects.equals(field1, that.field1); // field2 missing } public int hashCode() { return Objects.hash(field1, field2); } } ]]> </example> </rule> <rule name="HashCodeOnlyCallsSuper" language="java" message="hashCode only calls super." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isio03"> <description>Problem: when equals is implemented, hashCode needs to be implemented, too; and if objects are equal, hashCode needs to be equal, too. If hashCode only calls super.hashCode, it is effectively not implemented. Strange things can happen, for instance, if super.hashCode calls Object.hashCode, it is unique for each object and when used in Set or as key in Map, equal objects can duplicate in Set, and keys not found in Map. Solution: implement hashCode properly, consistent with equals. Meet the equals and hashCode contracts. If objects are equal, hashCode needs to be equal, too. See Effective Java. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="correctness,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration[@Name='equals' and FormalParameters/@Size=1 and @Static=false() and ModifierList/@ExplicitModifiers='public'] /Block[not(not(.//FieldAccess) and count(descendant::MethodCall)=1 and .//MethodCall[pmd-java:matchesSig('java.lang.Object#equals(_)')]/SuperExpression)] /ancestor::ClassBody[1]//MethodDeclaration[@Name='hashCode' and FormalParameters/@Size=0 and @Static=false() and ModifierList/@ExplicitModifiers='public'] /Block[count(descendant::MethodCall)=1][.//MethodCall[pmd-java:matchesSig('java.lang.Object#hashCode()')]/SuperExpression] ]]></value> </property> </properties> <example> <![CDATA[ class Bad { String field1; public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Bad that = (Bad) o; return Objects.equals(field1, that.field1); } public int hashCode() { return super.hashCode(); // bad } } class Good { String field1; public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Bad1 that = (Bad1) o; return Objects.equals(field1, that.field1); } public int hashCode() { return Objects.hash(field1); // good } } ]]> </example> </rule> <rule name="ImplementEqualsHashCodeOnValueObjects" message="Equals and/or hashCode is missing for a value object." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#incorrect-equals-and-hashcode"> <description> Problem: If equals and hashCode are not defined, they don't meet the programmer's expectations and the requirements for use with the collections API. It may result in unexpected, undesired behavior. Solution: Add proper equals and hashCode methods that meet the equals-hashCode contract to all objects which might anyhow be put in a Map, Set or other collection. Or use Lombok @EqualsAndHashCode, @Value or @Data. Also holds for inner classes. If the object should never be checked for equality or used in a collection, also add those methods and let them throw UnsupportedOperationException to fail fast. @Xml... , @Entity, Throwable and Executor and some other objects are ignored because they are assumed to be not used as value objects. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="jpinpoint-rule,unpredictable" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ (:- top level class :) //ClassDeclaration (: exclusions, the following annotations do implement equals and hashCode -:) [not(pmd-java:hasAnnotation('lombok.Data') or pmd-java:hasAnnotation('lombok.Value')or pmd-java:hasAnnotation('lombok.EqualsAndHashCode') (: exclusions, the following annotations assume a non-value object -:) or pmd-java:hasAnnotation('javax.ejb.Singleton') or pmd-java:hasAnnotation('jakarta.ejb.Singleton') or pmd-java:hasAnnotation('org.springframework.stereotype.Component') or pmd-java:hasAnnotation('org.springframework.stereotype.Controller') or pmd-java:hasAnnotation('org.springframework.stereotype.Service') or pmd-java:hasAnnotation('org.springframework.stereotype.Repository') or pmd-java:hasAnnotation('org.springframework.context.annotation.Configuration') or pmd-java:hasAnnotation('org.springframework.web.bind.annotation.RestController') or pmd-java:hasAnnotation('org.springframework.web.bind.annotation.ControllerAdvice') or pmd-java:hasAnnotation('org.springframework.ws.server.endpoint.annotation.Endpoint') or pmd-java:hasAnnotation('javax.persistence.Entity') or pmd-java:hasAnnotation('jakarta.persistence.Entity') or pmd-java:hasAnnotation('javax.persistence.Embeddable') or pmd-java:hasAnnotation('jakarta.persistence.Embeddable') or pmd-java:hasAnnotation('javax.persistence.MappedSuperclass') or pmd-java:hasAnnotation('jakarta.persistence.MappedSuperclass') (: JAXB annotations: @XmlType, @XmlRootElement, @XmlAccessorType, @XmlAccessorOrder :) or ModifierList/Annotation[starts-with(@SimpleName, 'Xml')] )] [@Interface=false() and @Abstract=false() and not( (: excluded types, assumed non-value objects :) pmd-java:typeIs('java.util.concurrent.Executor') or pmd-java:typeIs('java.lang.Throwable') or pmd-java:typeIs('javax.ws.rs.ext.ContextResolver') or pmd-java:typeIs('javax.ws.rs.client.ClientRequestFilter') )] /ClassBody[FieldDeclaration[@Static=false() and (not (pmd-java:hasAnnotation('javax.xml.bind.annotation.XmlElement'))) and (: a getter with result type of a field :) (ancestor::ClassBody[1]/MethodDeclaration [(@Visibility='public' or ../../@Nested=true()) and @Static=false() and (starts-with(@Name, 'get') and string-length(@Name) > 3 or starts-with(@Name, 'is') and string-length(@Name) > 2)] [ClassType/@SimpleName=ancestor::ClassBody[1]/FieldDeclaration[@Static=false()]/ClassType/@SimpleName] ) (: and class has no equals nor hashCode method :) and (not (ancestor::ClassBody[1]/MethodDeclaration[(@Visibility='public' or ../../@Nested=true()) and @Static=false() and (@Name='equals' and .//FormalParameters[@Size=1]) or (@Name='hashCode' and .//FormalParameters[@Size=0])])) (: and class has a toString and #fields <= 1+#getters :) and ((ancestor::ClassBody[1]//MethodDeclaration[(@Visibility='public' or ../../@Nested=true()) and @Static=false() and @Name='toString'] and count(ancestor::ClassBody[1]/FieldDeclaration[@Static=false()]) <= (1 + count(ancestor::ClassBody[1]/MethodDeclaration[(@Visibility='public' or ../../@Nested=true()) and @Static=false() and (starts-with(@Name, 'get') and string-length(@Name) > 3 or starts-with(@Name, 'is') and string-length(@Name) > 2)])) or ancestor::ClassDeclaration[1][ends-with(upper-case(@SimpleName), 'DTO')]) (: or #fields == #getters :) or count(ancestor::ClassBody[1]/FieldDeclaration[@Static=false()]) = count(ancestor::ClassBody[1]/MethodDeclaration[(@Visibility='public' or ../../@Nested=true()) and @Static=false() and (starts-with(@Name, 'get') and string-length(@Name) > 3 or starts-with(@Name, 'is') and string-length(@Name) > 2)]) ) ] (:-up to class -:) ]/.. ]]> </value> </property> </properties> <example> <![CDATA[ class Getters { // bad private String someState1 = "some1"; private String someState2 = "some2"; public String getSomeState1() { return someState1; } public String getSomeState2() { return someState2; } } class Good { private String someState1 = "some1"; private String someState2 = "some2"; public String getSomeState1() { return someState1; } public String getSomeState2() { return someState2; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Good good = (Good) o; return Objects.equals(someState1, good.someState1) && Objects.equals(someState2, good.someState2); } @Override public int hashCode() { return Objects.hash(someState1, someState2); } } ]]> </example> </rule> <rule name="ImproperVariableName" language="java" message="Give variables and fields a meaningful name." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#m01"> <description>Problem: Variables like 'var3' and fields like 'FOUR = 4', do not express what they are used for. This is bad for maintainability. Solution: Let variable names express what they are used for, like 'key' and 'MAX_KEYS = 4' (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="bad-practice,confusing,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //VariableId[matches(@Name, 'VALUE|VAL|INTEGER|INT|LONG|NUMBER|NUM|[0-9]+|ZERO|ONE|TWO|THREE|FOUR|FIVE|SIX|SEVEN|EIGHT|NINE|TEN|TWENTY', 'i') (: not allowed only as uppercase :) and (replace(@Name, 'VALUE|VAL|NUMBER|NUM|[0-9]+|ZERO|ONE|TWO|THREE|FOUR|FIVE|SIX|SEVEN|EIGHT|NINE|TEN|TWENTY|_', '') = '' (: not allowed as lower and uppercase :) or replace(@Name, 'INTEGER|INT|LONG|[0-9]+|ZERO|ONE|TWO|THREE|FOUR|FIVE|SIX|SEVEN|EIGHT|NINE|TEN|TWENTY|_', '', 'i') = '') or starts-with(@Name, 'var') and number(substring-after(@Name, 'var'))] ]]></value> </property> </properties> <example> <![CDATA[ class Foo { private static final int FOUR_ZERO_NINE_SIX = 4096; // bad private static final int VALUE_42 = 42; // bad private static int six = 6; // bad private int five = 7; // what? bad private static final int SIXTIES_START = 1960; // good void bar() { String var1 = "baz"; // bad } } ]]> </example> </rule> <rule name="MDCPutWithoutRemove" message="MDC put is used without finally remove." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#IL04"> <description> MDC values are added for logging, but not removed. Problem: MDC values can leak to other user transactions (requests) and log incorrect information. Solution: remove the MDC value in a finally clause. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="correctness,jpinpoint-rule,performance" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //MethodCall[pmd-java:matchesSig('org.slf4j.MDC#put(java.lang.String,java.lang.String)')]/ArgumentList/*[1][self::StringLiteral or self::VariableAccess] [not(@Image=ancestor::MethodDeclaration//TryStatement//FinallyClause//MethodCall[pmd-java:matchesSig('org.slf4j.MDC#remove(java.lang.String)')]/ArgumentList/*[1]/@Image or ancestor::MethodDeclaration//TryStatement//FinallyClause//MethodCall[pmd-java:matchesSig('org.slf4j.MDC#clear()')] or @Image=ancestor::ClassBody//MethodDeclaration[@Name='afterCompletion' or @Name='postHandle']//MethodCall[pmd-java:matchesSig('org.slf4j.MDC#remove(java.lang.String)')]/ArgumentList/*[1]/@Image or ancestor::ClassBody//MethodDeclaration[@Name='afterCompletion' or @Name='postHandle']//MethodCall[pmd-java:matchesSig('org.slf4j.MDC#clear()')] (: Issue 326, doFinally, not sure to accept :) (:or @Image=ancestor::MethodDeclaration//MethodCall[@MethodName='doFinally']//MethodCall[pmd-java:matchesSig('org.slf4j.MDC#remove(java.lang.String)')]/ArgumentList/*[1]/@Image or ancestor::MethodDeclaration//MethodCall[@MethodName='doFinally']//MethodCall[pmd-java:matchesSig('org.slf4j.MDC#clear()')]:) (: Issue 473, a put in a method only called from a try block with a remove of that key, or a clear :) (: a put of a key not in a try statement nor with a doFinally :) or not(ancestor::TryStatement) and not(.//MethodCall[@MethodName='doFinally']) (: if method is non-private -> violation :) and not ((ancestor::MethodDeclaration/@Visibility!='private' (: or in a method called from a non-try scope -> violation:) or ancestor::MethodDeclaration/@Name = ancestor::ClassBody//MethodDeclaration[not(.//TryStatement)]//MethodCall/@MethodName) (: or in a method not called from a try statement with a finally -> violation :) or not(ancestor::MethodDeclaration/@Name=ancestor::ClassBody//TryStatement[.//FinallyClause]/Block//MethodCall/@MethodName) (: or in a method called from a try statement and the key not removed / cleared in finally :) or (ancestor::MethodDeclaration/@Name=ancestor::ClassBody//TryStatement[.//FinallyClause]/Block//MethodCall/@MethodName and not(@Image = ancestor::ClassBody//TryStatement/FinallyClause//MethodCall[pmd-java:matchesSig('org.slf4j.MDC#remove(java.lang.String)')]/ArgumentList/*[1]/@Image or ancestor::ClassBody//TryStatement//FinallyClause//MethodCall[pmd-java:matchesSig('org.slf4j.MDC#clear()')] ))) )] ]]> </value> </property> </properties> <example> <![CDATA[ class Bad { public void bad() { MDC.put("levelKey1", "levelName1"); // bad, not removed in finally MDC.put("levelKey2", "levelName2"); // bad, not removed at all doStuff(); MDC.remove("levelKey1"); } } class Good { public void good() { try { MDC.put("levelKey1", "levelName1"); MDC.put("levelKey2", "levelName2"); doStuff(); } finally { MDC.remove("levelKey1"); MDC.remove("levelKey2"); } } } ]]> </example> </rule> <rule name="MissingEqualsAndHashCodeWithGetterSetter" language="java" message="Equals and/or hashCode is missing in a class with lombok @Getter or @Setter." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#ieah01"> <description>Problem: If only the @Setter and/or @Getter annotation is used, and equals and hashcode are not defined, this is probably a mistake. The object has fields and if used in a collection or with equality, it will not behave as expected. Solution: include equals and hashCode e.g. by @EqualsAndHashCode or usually better @Data instead of @Setter/@Getter, however, are setters really needed? Prefer immutability, so @Value, combined with @Builder, with @Singular for collection fields. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="xpath"> <value><![CDATA[ //ClassDeclaration[ (pmd-java:hasAnnotation('lombok.Getter') or pmd-java:hasAnnotation('lombok.Setter')) and not( pmd-java:hasAnnotation('lombok.EqualsAndHashCode') or pmd-java:hasAnnotation('org.springframework.context.annotation.Configuration') or pmd-java:hasAnnotation('org.springframework.boot.context.properties.ConfigurationProperties') or pmd-java:hasAnnotation('jakarta.persistence.Entity') or pmd-java:hasAnnotation('javax.persistence.Entity') or pmd-java:typeIs('java.lang.Throwable') or ( ./ClassBody/MethodDeclaration[@Name='equals' and @Arity=1 and ./PrimitiveType[@Kind='boolean']] and ./ClassBody/MethodDeclaration[@Name='hashCode' and @Arity=0 and ./PrimitiveType[@Kind='int']] ) ) ]/ModifierList/Annotation[1] ]]></value> </property> <property name="tags" value="jpinpoint-rule,correctness" type="String" description="classification"/> </properties> <example> <![CDATA[ import lombok.*; @Getter @Setter // no equals/hashcode annotation class MissingEqualsHashCodeWithGetterSetterBad { // bad String field1; String field2; // no equals, no hashCode } @Getter @Setter @EqualsAndHashCode class EqualsHashCodeWithGetterSetterOkay { String field1; String field2; } @Builder @Value class ValueGood { String field1; String field2; } ]]> </example> </rule> <rule name="MissingFieldInEquals" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Field simply assigned to is missing in equals method." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#ieah04"> <description>Problem: If a field which can be assigned separately (independent of other fields) is missing in the equals method, then changing the field in one object has no effect on the equality with another object. However, if a field of one of two equal objects is changed, the expectation is that they are no longer equal. Solution: include the missing field in the equals and hashCode method. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="jpinpoint-rule,suspicious" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //FieldDeclaration[@Static=false()]//VariableId[ (: names used as left hand side in simple assignments in methods or constructor matching fields :) (@Name = ancestor::ClassBody[1]/(MethodDeclaration|ConstructorDeclaration)/Block//AssignmentExpression[not(InfixExpression or ConstructorCall or MethodCall or FieldAccess/TypeExpression)] /(VariableAccess|FieldAccess)[1]/@Name) and (: with an equals method defined at that level with 1 argument and not having a throw statement :) ancestor::ClassBody[1]/MethodDeclaration[@Name='equals' and FormalParameters/@Size=1 and not (Block/ThrowStatement)] (: where names used in equals method matching fields are non-equal to :) and not(@Name = ancestor::ClassBody[1]/MethodDeclaration [@Name='equals' and FormalParameters/@Size=1]/Block//(VariableAccess|FieldAccess)/@Name) ] ]]> </value> </property> </properties> <example> <![CDATA[ class Bad1 { String field1; String field2; // bad, missing in equals public Bad1(String arg2) { field2 = arg2; } public void setField1(String arg1) { field1 = arg1; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EqHashTryout2 that = (EqHashTryout2) o; return Objects.equals(field1, that.field1); } public int hashCode() { return Objects.hash(field1); } } ]]> </example> </rule> <rule name="MissingFieldInHashCode" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Equals uses a field which is missing in hashCode." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#ieah03"> <description>Problem: Two unequal objects can have the same hashCode and end up in the same bucket of a Map. This may result in bad performance, O(n) lookup instead of O(1). Solution: Use the same fields in hashCode as are used in equals. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,performance" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //FieldDeclaration/VariableDeclarator[ (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='equals' and FormalParameters/@Size = 1]/Block//(FieldAccess|VariableAccess)/@Name) > (@Name = ancestor::ClassBody[1]//MethodDeclaration[@Name='hashCode' and FormalParameters/@Size = 0]/Block//(FieldAccess|VariableAccess)/@Name) ]/VariableId ]]> </value> </property> </properties> <example> <![CDATA[ class Good { String field1, field2; public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EqHashTryout2 that = (EqHashTryout2) o; return Objects.equals(field1, that.field1) && Objects.equals(field2, that.field2); } public int hashCode() { return Objects.hash(field1, field2); } } class Bad { String field1; String field2; //bad - missing in hashCode public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EqHashTryout2 that = (EqHashTryout2) o; return Objects.equals(field1, that.field1) && Objects.equals(field2, that.field2); } public int hashCode() { int result = field1 != null ? field1.hashCode() : 0; return result; } } ]]> </example> </rule> <rule name="NonComparableMapKeys" language="java" message="Map keys should implement Comparable in order to make access fast." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuoc08"> <description>Problem: If multiple entries end up in the same HashMap bucket, they are stored as LinkedList, and with more than 7 as a red black tree. The list access time is O(n) and tree access time is only O(log n) which is much faster for large n. This tree implementation utilizes the compareTo from the Comparable interface. If this is not implemented, access will be slow. Solution: Implement Comparable for your Map keys. Do not use classes for the keys which don't implement Comparable, like Thread, Class and Object. At least not for Maps which can grow large. Note that equals and hashCode must be implemented properly for the keys, and compareTo must be compatible with equals. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: check key type in declaration :) //VariableDeclarator/../ClassType[pmd-java:typeIs('java.util.Map') and not(..//ConstructorCall[pmd-java:matchesSig('java.util.TreeMap#new(java.util.Comparator)')])] (: first type argument does not implement Comparable :) /TypeArguments/ClassType[1][not(pmd-java:typeIs('java.lang.Comparable')) ] , (: for when key type is missing in declaration (old style), check Map.put :) //MethodCall[pmd-java:matchesSig('java.util.Map#put(_,_)') and not( (: if Map is TreeMap constructed with a Comparator, it is also fine :) ./VariableAccess/@Name = ancestor::ClassDeclaration//FieldDeclaration[ .//ConstructorCall[pmd-java:matchesSig('java.util.TreeMap#new(java.util.Comparator)')] ]/VariableDeclarator/VariableId/@Name) ] (: first arg does not implement Comparable :) /ArgumentList/*[1][not(pmd-java:typeIs('java.lang.Comparable'))] [not(pmd-java:typeIs('byte'))][not(pmd-java:typeIs('short'))] [not(pmd-java:typeIs('int'))][not(pmd-java:typeIs('long'))] [not(pmd-java:typeIs('float'))][not(pmd-java:typeIs('double'))] [not(pmd-java:typeIs('boolean'))][not(pmd-java:typeIs('char'))] ]]></value> </property> </properties> <example> <![CDATA[ class Foo { Map<Object, String> oMap; // bad, Object does not implement Comparable Map<Thread, String> tMap; // bad, Thread does not implement Comparable Map oldStyleMap = new HashMap(); // cannot check here void putInOldStyleBad() { oldStyleMap.put(new Thread(), "value"); // bad } Map<Comparable, String> cMap; // good void putInOldStyleGood() { oldStyleMap.put("key", "value"); } } ]]> </example> </rule> <rule name="NonComparableSetElements" language="java" message="Set elements should implement Comparable in order to make access by element fast." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuoc09"> <description>Problem: A Set is implemented with a Map. If multiple entries end up in the same HashMap bucket, they are stored as LinkedList, and with more than 7 as a red-black tree. The list access time is O(n) and tree access time is only O(log n) which is much faster for large n. This tree implementation utilizes the compareTo from the Comparable interface. If this is not implemented, access by element will be slow. Iterating through the elements does not suffer from this slow access because no lookup by key in the map is involved. Access methods by element which are affected: contains[All], retainAll, remove[All]. Solution: Implement Comparable for your own Set elements. Avoid using large Sets with elements of types which don't implement Comparable, like Thread, Class and Object. At least when using access by element. Note that equals and hashCode must be implemented properly for the elements, and compareTo must be compatible with equals. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: declared variable used in Set by-element access :) //VariableId[@Name = ancestor::ClassBody//MethodCall[@MethodName = ('contains','containsAll','remove','removeAll','retainAll')]/VariableAccess/@Name] (: for 3 ways defined as a Set :) [(ancestor::FieldDeclaration|ancestor::LocalVariableDeclaration|ancestor::FormalParameter)/ClassType[pmd-java:typeIs('java.util.Set') (: not when Set constructed with Comparator :) and not(..//ConstructorCall[pmd-java:matchesSig('java.util.TreeSet#new(java.util.Comparator)')]) ] (: element type does not implement Comparable :) /TypeArguments/ClassType[not(pmd-java:typeIs('java.lang.Comparable'))] ]/../.. ]]></value> </property> </properties> <example> <![CDATA[ import java.util.*; import org.apache.hc.client5.http.HttpRoute; // does implement equals/hashCode yet *not* compareTo class Foo { Set<String> strSet = new HashSet<>(); List<String> strList = new ArrayList<>(); Set<HttpRoute> fieldRouteSet = new HashSet<>(); // bad List<HttpRoute> routeList = new ArrayList<>(); void byElemBad(Set<HttpRoute> paramRouteSet) { // bad paramRouteSet.retainAll(routeList); fieldRouteSet.contains(t); Set<HttpRoute> localRouteSet = new HashSet<>(); // bad localRouteSet.removeAll(routeList); } void otherCasesGood(Set<HttpRoute> paramRouteSet) { strSet.contains("bla"); strSet.retainAll(strList); strSet.remove("other"); paramRouteSet.iterator().next(); } } ]]> </example> </rule> <rule name="OptimizeMapOrSetForEnum" language="java" message="Map has enum keys or Set has enum elements, use EnumMap or EnumSet for more efficiency." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuoc03"> <description>Problem: A HashMap and HashSet are rather greedy in memory usage. Solution: Use an EnumMap or EnumSet. It is represented internally with arrays which is extremely compact and efficient. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,memory,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //(FieldDeclaration|LocalVariableDeclaration)[ ClassType[(pmd-java:typeIs('java.util.Map') and not (pmd-java:typeIs('java.util.EnumMap'))) or (pmd-java:typeIs('java.util.Set') and not(pmd-java:typeIs('java.util.EnumSet')))] /TypeArguments/ClassType[1][pmd-java:typeIs('java.lang.Enum') or @SimpleName = //EnumDeclaration/@SimpleName] and VariableDeclarator/(MethodCall|ConstructorCall) [not ((pmd-java:typeIs('java.util.EnumSet') or pmd-java:typeIs('java.util.EnumMap')) or (: Set.of() with 1 or 2 elems and Map.of() with 1 key+value don't have the HashSet/Map overhead :) (@MethodName='of' and ((pmd-java:typeIsExactly('java.util.Set') and ArgumentList/@Size <= 2) or (pmd-java:typeIsExactly('java.util.Map') and ArgumentList/@Size <= 2))) or (: it is exactly a Map or Set, cannot determine implementation #194 :) ((@MethodName!='of' and (pmd-java:typeIsExactly('java.util.Set') or pmd-java:typeIsExactly('java.util.Map')))) (: cannot determine type, indirect #194 :) or (not(pmd-java:typeIs('java.util.Set') or pmd-java:typeIs('java.util.Map'))) )] ] | //Initializer//AssignmentExpression[(pmd-java:typeIs('java.util.Map') and not (pmd-java:typeIs('java.util.EnumMap'))) or (pmd-java:typeIs('java.util.Set') and not(pmd-java:typeIs('java.util.EnumSet')))] [VariableAccess/@Name = ancestor::ClassBody/FieldDeclaration[ClassType/TypeArguments/ClassType[1][pmd-java:typeIs('java.lang.Enum') or @SimpleName = //EnumDeclaration/@SimpleName]]/VariableDeclarator/@Name] /(MethodCall|ConstructorCall) [not ((pmd-java:typeIs('java.util.EnumSet') or pmd-java:typeIs('java.util.EnumMap')) or (: Set.of() with 1 or 2 elems and Map.of() with 1 key+value don't have the HashSet/Map overhead :) (@MethodName='of' and ((pmd-java:typeIsExactly('java.util.Set') and ArgumentList/@Size <= 2) or (pmd-java:typeIsExactly('java.util.Map') and ArgumentList/@Size <= 2))) or (: it is exactly a Map or Set, cannot determine implementation #194 :) ((@MethodName!='of' and (pmd-java:typeIsExactly('java.util.Set') or pmd-java:typeIsExactly('java.util.Map')))) (: cannot determine type, indirect #194 :) or (not(pmd-java:typeIs('java.util.Set') or pmd-java:typeIs('java.util.Map'))) ) ] ]]></value> </property> </properties> <example> <![CDATA[ Map<YourEnumType, String> map = new EnumMap<>(YourEnumType.class); Set<YourEnumType> set = EnumSet.allOf(YourEnumType.class); ]]> </example> </rule> <rule name="UnconditionalConcatInLogArgument" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="String concatenation (+) is executed regardless of log level and can be expensive" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#il01"> <description>Problem: String concatenation (+) is executed regardless of log level and can be expensive. Solution: Use SLF4J formatting with {}-placeholders or log and format conditionally. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall [@MethodName=('trace','debug','info')] [ (: supported logger libs :) (VariableAccess[ pmd-java:typeIs('org.slf4j.Logger') or pmd-java:typeIs('java.util.logging.Logger') or pmd-java:typeIs('org.apache.commons.logging.Log') ]) or (: Slfj4 annotation makes the log variable AmbiguousName as there is no explicit log field :) (AmbiguousName[ancestor::ClassDeclaration[pmd-java:hasAnnotation('lombok.extern.slf4j.Slf4j')]]) ] /ArgumentList[.//InfixExpression[@Operator='+'][(VariableAccess|MethodCall)[pmd-java:typeIs('java.lang.String')] or (StringLiteral and (VariableAccess or MethodCall))]] [not(ancestor::IfStatement//MethodCall[ @MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable') and count(../../InfixExpression[@Operator='||' or @Operator='|' or @Operator='^']) = 0 ]) ] ]]></value> </property> </properties> <example> <![CDATA[ @slf4j class Foo { void bad(String a, String b) { log.debug("message: {}", a + b); // bad } void good(String a, String b) { log.debug("message: {}-{}", a, b); // good } } ]]> </example> </rule> <rule name="UnconditionalCreatedLogArguments" language="java" message="Avoid unconditional creation of a log argument, it may not be needed." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#il06"> <description>Problem: Creation of a log argument with a toString or other operation(s) may be expensive, while depending on the log level, the result may not be used. Solution: Create the log argument conditionally on the log level, within an if statement. For just 'obj.toString()', just pass 'obj' to the log method and leave it to SLF4J to call toString() only if needed. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall (: log levels typically/often not enabled :) [@MethodName=('trace','debug','info')] [ (: supported logger libs :) (VariableAccess[ pmd-java:typeIs('org.slf4j.Logger') or pmd-java:typeIs('java.util.logging.Logger') or pmd-java:typeIs('org.apache.commons.logging.Log') ]) or (: Slfj4 annotation makes the log variable AmbiguousName as there is no explicit log field :) (AmbiguousName[ancestor::ClassDeclaration[pmd-java:hasAnnotation('lombok.extern.slf4j.Slf4j')]]) ] (: no violation if conditionally: only executed if level enabled is okay :) [not(ancestor::IfStatement//MethodCall[ @MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable') and count(../../InfixExpression[@Operator='||' or @Operator='|' or @Operator='^']) = 0 ]) ] (: no violation if return conditionally before :) [not(ancestor::ExpressionStatement/preceding-sibling::IfStatement[.//ReturnStatement and .//MethodCall[@MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable')]])] (: equals a declared var, with initializer not like obj.get[Xxx], we assume a simple getter is quick; except when in a loop :) /ArgumentList//VariableAccess [@Name = ancestor::MethodDeclaration//VariableDeclarator[not(MethodCall[starts-with(@MethodName, 'get')] and not(ancestor::WhileStatement or ancestor::ForStatement or ancestor::DoStatement))]/VariableId/@Name] (: and it is not used in a return :) [not (@Name = ancestor::MethodDeclaration//VariableAccess[not(ancestor::MethodCall[@MethodName=('trace','debug','info')])]/@Name)] (: except if method name starts with 'log', and it is non-public, .. :) [not(ancestor::MethodDeclaration [starts-with(@Name, 'log') and @Visibility = 'private'] (: and the method is called only conditionally: it is called inside an if :) [@Name=ancestor::ClassBody//MethodCall[ancestor::IfStatement//MethodCall[ @MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable') and count(../../InfixExpression[@Operator='||' or @Operator='|' or @Operator='^']) = 0 ]]/@MethodName] (: and it is not called not inside an if :) [not(@Name=ancestor::ClassBody//MethodCall[not(ancestor::IfStatement//MethodCall[@MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable')])]/@MethodName)] )] ]]></value> </property> </properties> <example> <![CDATA[ class Foo { void bad(Object obj) { String logObj = obj.toString(); log.debug("object to String: {}", logObj); //bad } void good(Object obj) { log.debug("object to String: {}", obj); //good } } ]]> </example> </rule> <rule name="UnconditionalOperationOnLogArgument" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Operation is executed regardless of log level and can be expensive" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#il03"> <description>Problem: An operation is executed regardless of log level. This could be much processing while the result is typically not used. Detected are obj.toString() and operations with one or more arguments except usually cheap obj.getXXX(arg). Solution: Execute the operation only conditionally and utilize SLF4J formatting with {}-placeholders. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall (: log levels often not enabled in prod :) [@MethodName=('trace','debug','info')] [ (: supported logger libs :) (VariableAccess[ pmd-java:typeIs('org.slf4j.Logger') or pmd-java:typeIs('java.util.logging.Logger') or pmd-java:typeIs('org.apache.commons.logging.Log') ]) or (: Slfj4 annotation makes the log variable AmbiguousName as there is no explicit log field :) (AmbiguousName[ancestor::ClassDeclaration[pmd-java:hasAnnotation('lombok.extern.slf4j.Slf4j')]]) ] (: only if unconditional, and not || before the log check in the if statement :) [not(ancestor::IfStatement//MethodCall[ @MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable') and count(../../InfixExpression[@Operator='||' or @Operator='|' or @Operator='^']) = 0 ]) ] (: and not returned conditionally before :) [not(ancestor::ExpressionStatement/preceding-sibling::IfStatement[.//ReturnStatement and .//MethodCall[@MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable')]])] (: for operations in log with >0 arguments or toString(); except getters which are assumed as fast :) /ArgumentList//MethodCall[ (ArgumentList[@Size>0] or @MethodName='toString') and not(starts-with(@MethodName, 'get')) (: if methods are chained, only report 1 in the chain :) and not(ancestor::MethodCall[ArgumentList[@Size>0]]/MethodCall) ] (: except if method name contains log, trace, debug or info (case insensitive), and it is non-public :) [not(ancestor::MethodDeclaration [matches(@Name, 'trace|debug|info|log','i') and @Visibility = 'private'] (: and the method is called only conditionally: it is called inside an if and not prefixed with || :) [@Name=ancestor::ClassBody//MethodCall[ancestor::IfStatement//MethodCall[ @MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable') and count(../../InfixExpression[@Operator='||' or @Operator='|' or @Operator='^']) = 0 ]]/@MethodName] (: and it is not called not inside an if :) [not(@Name=ancestor::ClassBody//MethodCall[ not( ancestor::IfStatement//MethodCall[ @MethodName = ('isTraceEnabled', 'isDebugEnabled','isInfoEnabled', 'isLoggable') and count(../../InfixExpression[@Operator='||' or @Operator='|' or @Operator='^']) = 0 ] ) ]/@MethodName) ] )] ]]> </value> </property> </properties> <example> <![CDATA[ LOG.debug("customer = {}", customer.toString()); // bad LOG.debug("Complete Soap response: {}", getSoapMsgAsString(context.getMessage())); // bad LOG.debug("customer = {}", customer); // good if (LOG.isDebugEnabled()) { // good LOG.debug("Complete Soap response: {}", getSoapMsgAsString(context.getMessage())); } ]]> </example> </rule> <rule name="UsingSuppressWarnings" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Using SuppressWarnings." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md"> <description>Problem: The suppressed rule detects problems, suppressing the detected violations without full knowledge can lead to the problems the rule is trying to prevent. Solution: Only suppress violations (warnings) when you have complete understanding of the rule, and you are sure it does not apply. When suppressing warnings, document your reasoning and report false positives to the rule maintainers to help improve rule accuracy. Note: This rule is just informational to track use of SuppressWarnings, to be able to double-check if suppression is correct. It often doesn't need a fix. (jpinpoint-rules)</description> <priority>5</priority> <properties> <property name="tags" value="jpinpoint-rule,suspicious" type="String" description="classification"/> <property name="ruleIdMatches" type="String" value=".*" description="Regex for inclusion of rules"/> <property name="ruleIdNotMatches" type="String" value="^$" description="Regex for exclusion of rules"/> <property name="xpath"> <value><![CDATA[ //Annotation[@SimpleName=("SuppressWarnings","SuppressFBWarnings")]//StringLiteral[(matches(@Image, $ruleIdMatches)) and not (matches(@Image, $ruleIdNotMatches))] ]]> </value> </property> </properties> <example> <![CDATA[ @SuppressWarnings("pmd:UnconditionalCreatedLogArguments") // Reason for suppression: the rule doesn't recognize record getters (yet), and those are not expensive public void process(ConsumerRecord consRecord) { ConsumerEvent event = consRecord.event(); // record getter log.info("Received event '{}'", event); // pmd:UnconditionalCreatedLogArguments suppressed } ]]> </example> </rule> <rule name="UsingSuppressWarningsHighRisk" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Using SuppressWarnings for a rule that is meant to prevent high risk problems." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md"> <description>Problem: The suppressed rule detects high risk problems, suppressing the detected violations without full knowledge can lead to incidents like customer data mix-up, corrupt data, server crashes or very bad performance. Solution: Only suppress violations (warnings) when you have complete understanding of the rule, and you are sure it does not apply. When suppressing warnings, document your reasoning and report false positives to the rule maintainers to help improve rule accuracy. Note: This rule is to track use of SuppressWarnings for high risk rule violations, to be able to double-check if suppression is correct. It doesn't necessarily need a fix. (jpinpoint-rules)</description> <priority>4</priority> <properties> <property name="tags" value="data-mix-up,jpinpoint-rule,suspicious" type="String" description="classification"/> <property name="ruleIdMatches" type="String" value="AvoidUserDataInSharedObjects|AvoidUnguardedMutableFieldsInSharedObjects|AvoidUnguardedAssignmentToNonFinalFieldsInSharedObjects|AvoidMutableStaticFields|[^\w]ALL[^\w]|[^\w]all[^\w]|PMD[^\.]|pmd[^:]" description="Regex for inclusion of high risk rules"/> <property name="ruleIdNotMatches" type="String" value="^$" description="Regex for exclusion of high risk rules"/> <property name="xpath"> <value><![CDATA[ //Annotation[@SimpleName=("SuppressWarnings","SuppressFBWarnings")]//StringLiteral[(matches(@Image, $ruleIdMatches)) and not (matches(@Image, $ruleIdNotMatches))] ]]> </value> </property> </properties> <example> <![CDATA[ @SuppressWarnings({"pmd:AvoidUnguardedMutableFieldsInSharedObjects", "PMD.AvoidUserDataInSharedObjects"}) // suspicious, bad to suppress @Component @Data class VMRData { private List<OrderDetails> customerOrderList; // a bad idea to have in a singleton, should be solved instead of suppressed //.. } ]]> </example> </rule> <!-- END Included file 'common.xml' --> <!-- BEGIN Included file 'common_std.xml' --> <rule name="AvoidApacheCommonsFileItemNonStreaming" language="java" message="Avoid memory intensive FileItem.get and FileItem.getString" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isio03"> <description> Problem: Use of FileItem.get and FileItem.getString could exhaust memory since they load the entire file into memory Solution: Use streaming methods and buffering. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="io,jpinpoint-rule,memory,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //MethodCall[@MethodName="get" or @MethodName="getString"][VariableAccess[pmd-java:typeIs('org.apache.commons.fileupload.FileItem')]] ]]> </value> </property> </properties> <example> <![CDATA[ public class FileStuff { private String bad(FileItem fileItem) { return fileItem.getString(); } private InputStream good(FileItem fileItem) { return fileItem.getInputStream(); } } ]]> </example> </rule> <rule name="AvoidCalendarDateCreation" language="java" message="A Calendar is used to create a Date or DateTime, this is expensive." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ue01"> <description>Problem: A Calendar is a heavyweight object and expensive to create. Solution: Use 'new Date()', Java 8+ java.time.[Local/Zoned]DateTime.now(). (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,memory,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //MethodCall[pmd-java:matchesSig('java.util.Calendar#getInstance()') or pmd-java:matchesSig('java.util.GregorianCalendar#getInstance()')][ancestor::MethodCall[@MethodName='getTime' or @MethodName='getTimeInMillis']] | //MethodCall[@MethodName='getTime' or @MethodName='getTimeInMillis'][VariableAccess[pmd-java:typeIs('java.util.Calendar')]] | //ConstructorCall/ClassType[pmd-java:typeIs('org.joda.time.DateTime')]/../ArgumentList//MethodCall[pmd-java:matchesSig('java.util.Calendar#getInstance()') or pmd-java:matchesSig('java.util.GregorianCalendar#getInstance()')] ]]> </value> </property> </properties> <example> <![CDATA[ public class DateStuff { private Date bad1() { return Calendar.getInstance().getTime(); // now } private Date good1a() { return new Date(); // now } private LocalDateTime good1b() { return LocalDateTime.now(); } private long bad2() { return Calendar.getInstance().getTimeInMillis(); } private long good2() { return System.currentTimeMillis(); } } ]]> </example> </rule> <rule name="AvoidConcatInAppend" language="java" message="Concatenation inside append. Use extra append." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isu04"> <description>Concatenation of Strings is used inside an StringBuilder.append argument. Problem: Each statement with one or more +-operators creates a hidden temporary StringBuilder, a char[] and a new String object, which all have to be garbage collected. Solution: Use an extra fluent append instead of concatenation. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //(ReturnStatement|ExpressionStatement|LocalVariableDeclaration) [.//MethodCall[pmd-java:matchesSig('java.lang.StringBuilder#append(java.lang.String)') or pmd-java:matchesSig('java.lang.StringBuffer#append(java.lang.String)')] //ArgumentList[./InfixExpression[@Operator="+"] /*[pmd-java:typeIs('java.lang.String')][count(..//VariableAccess) > 0 and not(..//VariableAccess/@Name = ancestor::ClassBody//VariableId[@Final=true()]/@Name)]] ] ]]></value> </property> </properties> <example> <![CDATA[ public class StringStuff { private String bad(String arg) { StringBuilder sb = new StringBuilder(); sb.append("arg='" + arg + "'"); return sb.toString(); } private String good(String arg) { StringBuilder sb = new StringBuilder(); sb.append("arg='").append(arg).append("'"); return sb.toString(); } } ]]> </example> </rule> <rule name="AvoidConcatInLoop" language="java" message="A String is concatenated in a loop. Use StringBuilder.append." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#isu02"> <description>A String is built in a loop by concatenation. Problem: Each statement with one or more +-operators creates a hidden temporary StringBuilder, a char[] and a new String object, which all have to be garbage collected. Solution: Use the StringBuilder append method. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (//ForStatement | //ForeachStatement | //WhileStatement | //DoStatement)//AssignmentExpression[ (: a += ...; - a being a string OR a = ... + a + ...; - a being a string :) @Operator = '+=' or (@Operator = '=' and ./InfixExpression[@Operator="+"]) ]/VariableAccess[pmd-java:typeIs('java.lang.String')] /.. (: Go up to report on the StatementExpression :) ]]> </value> </property> </properties> <example> <![CDATA[ public class StringStuff { private String bad(String arg) { String log = ""; List<String> values = Arrays.asList("tic ", "tac ", "toe "); for (String val : values) { log += val; } return log; } private String good(String arg) { StringBuilder sb = new StringBuilder(); List<String> values = Arrays.asList("tic ", "tac ", "toe "); for (String val : values) { sb.append(val); } return sb.toString(); } } ]]> </example> </rule> <rule name="AvoidForEachInStreams" language="java" message="Prefer side-effect-free functions in streams, use forEach only for logging." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#ils01"> <description>Problem: Streams is a paradigm based on functional programming: the result should depend only on its input and not update any state. Use of forEach is actually iterative code masquerading as streams code. It is typically harder to read and less maintainable than the iterative form. For parallel streams, side effects are dangerous: accessing a thread-unsafe shared variable is a concurrency bug. Solution: Use the for-each (enhanced-for) loop, internal iterator, or the pure functional form. The forEach operation should only be used to report (i.e. log) the result of a stream computation. (jpinpoint-rules)</description> <priority>4</priority> <properties> <property name="tags" value="bad-practice,jpinpoint-rule,pitfall" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: should match java.lang.Stream#forEach(java.util.function.Consumer) but also others like java.util.stream.IntStream#forEach(java.util.function.IntConsumer) :) //MethodCall[pmd-java:matchesSig('java.util.stream.BaseStream#forEach(_)')] [ArgumentList[not( .//LambdaExpression[ ( (: all expression statements in a lambda expression in a foreach should all be logging :) count(.//ExpressionStatement) = count( .//ExpressionStatement/MethodCall[ (: supported logger libs :) VariableAccess[pmd-java:typeIs('org.slf4j.Logger') or pmd-java:typeIs('java.util.logging.Logger') or pmd-java:typeIs('org.apache.commons.logging.Log') ] (: Slfj4 annotation makes the log variable AmbiguousName as there is no explicit log field :) or (AmbiguousName[@Name='log'] and ancestor::ClassDeclaration[pmd-java:hasAnnotation('lombok.extern.slf4j.Slf4j')])] ) + count(.//ExpressionStatement/MethodCall[starts-with(@MethodName,'log')]) ) (: and not a violation when it is a method call to do logging :) and not(MethodCall) or MethodCall[matches(@MethodName,'trace|debug|info|warn|error|log','i')] ] (: if a foreach has a method reference, it should be only for logging :) or .//MethodReference[ (TypeExpression/ClassType[pmd-java:typeIs('lombok.extern.java.Log')] (: supported logger libs :) or VariableAccess[pmd-java:typeIs('org.slf4j.Logger') or pmd-java:typeIs('java.util.logging.Logger') or pmd-java:typeIs('org.apache.commons.logging.Log')] (: Slfj4 annotation makes the log variable AmbiguousName as there is no explicit log field :) or (AmbiguousName[@Name='log'] and ancestor::ClassDeclaration[pmd-java:hasAnnotation('lombok.extern.slf4j.Slf4j')]) ) or starts-with(@MethodName,'log')] )]] ]]></value> </property> </properties> <example> <![CDATA[ List<String> letters = List.of("D", "O", "G", "E"); Map<String, String> map = new HashMap<>(); // forEach in stream, bad letters.stream().forEach(l -> map.put(l, 0)); // bad, side effect, modifies map // pure functional form, good map = letters.stream().collect(toMap(l -> l, v -> 0)); // reporting result by logging, good letters.stream().forEach(Log::info); // external iterative form, meant for modifying state, good for (String l : letters) { map.put(l, 0); } // internal iterator, fine for modifying state, good letters.forEach(l -> map.put(l, 0)); ]]> </example> </rule> <rule name="AvoidSideEffectsInStreams" language="java" message="Apply functional programming with streams, avoid side-effects." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#ils02"> <description>Problem: Streams is a paradigm based on functional programming: the result should depend only on its input, and the stream should not update any state: not modify any variable. By the spec, everything that does not contribute to the result, may be optimized away. So, side effects are not guaranteed to be executed! For parallel streams, side effects are dangerous: accessing a thread-unsafe shared variable is a concurrency bug. Accessing a (thread-safe) shared variable may cause data mix-up between the threads. Solution: Use the pure functional form: return a result based just on the input; do not modify any variable. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="bad-practice,data-mix-up,jpinpoint-rule,pitfall" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: matches java.lang.Stream and java.util.stream.IntStream like types :) //MethodCall[pmd-java:typeIs('java.util.stream.BaseStream')] (: modifying methods :) [ArgumentList[.//(MethodCall|MethodReference)[matches(@MethodName,"^(set|put|clear|add|remove|replace|retain)")]/VariableAccess or .//AssignmentExpression/ArrayAccess/*[1][pmd-java:nodeIs('VariableAccess')] ]] ]]></value> </property> </properties> <example> <![CDATA[ public List<String> getEndpointsInfo(String... endpoints) { AtomicReference<String> currentEndpoint = new AtomicReference<>(); return Arrays.stream(endpoints) .peek(endpoint -> currentEndpoint.set(endpoint)) // bad .peek(endpoint -> log.debug(endpoint)) // peek is meant for something like this .map(String::toLowerCase) .map(pingInfo -> addEndpointInfo(pingInfo, currentEndpoint.get())) .toList(); } ]]> </example> </rule> <rule name="LetFieldsMeetSerializable" language="java" message="Fields in a Serializable class should be serializable or transient." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#isr02"> <description>Problem: Field in a Serializable class is not serializable nor transient. When (de)serialization happens, a RuntimeException will be thrown and (de)serialization fails. Solution: make the field either transient, make its class implement Serializable or interface extend Serializable. Note: Classes extending Throwable do, by inheritance, implement Serializable, yet are excluded in this rule, since they are typically never actually serialized. An exception to this exception is when extending RemoteException, then fields should be transient or serializable. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,pitfall,replaces-sonar-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: non-transient, non-static, non-primitive fields :) //FieldDeclaration[ not(pmd-java:modifiers() = ('transient', 'static')) and not(.//PrimitiveType) and ClassType[not(pmd-java:typeIs('java.io.Serializable'))] (: and can be resolved :) and ClassType[pmd-java:typeIs('java.lang.Object')] (: if has type args, type args which are not serializable like List<String, Thread>) :) and ( not(exists(.//TypeArguments)) or exists(.//TypeArguments/ClassType[ not(pmd-java:typeIs('java.io.Serializable')) (: and can be resolved :) and pmd-java:typeIs('java.lang.Object') ] ) ) and ancestor::ClassDeclaration[pmd-java:typeIs('java.io.Serializable') (: Throwable is the exception, except RemoteException :) and not( pmd-java:typeIs('java.lang.Throwable') and not(pmd-java:typeIs('java.rmi.RemoteException')) ) ] ] ]]></value> </property> </properties> <example> <![CDATA[ class Foo implements Serializable { String meets; Exception exMeets; Serializable ser; Thread t1NotMeets; // bad, Thread is not serializable transient Thread t2meets; List<String> listOfStrings = new ArrayList(); List<Thread> listOfThreads = new ArrayList(); // bad Map<String,String> mapToString = new HashMap(); Map<String,Thread> mapToThread = new HashMap(); //bad } class Bar extends Exception { Thread t1NotMeets; transient Thread t2meets; } class Baz extends RemoteException { Thread t1NotMeets; // bad transient Thread t2meets; } ]]> </example> </rule> <rule name="LimitNestingInLambdas" language="java" message="Avoid deep nesting of lambdas in lambda expressions." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#isr05"> <description>Problem: lambda expressions with deep nesting (lambda's in lambda's) are hard to understand and maintain. Solution: extract the lambda expression code block(s) into one or more well-named separate method(s). Note: A violation when the depth of lambda-with-code-block nesting exceeds (by default) 1, or the depth of lambda-single-expression in lambda nesting exceeds (by default) 4. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="brain-overload,jpinpoint-rule,replaces-sonar-rule" type="String" description="classification"/> <property name="param-block-max" value="1" type="String" description="Maximum allowed depth of lambda-with-code-block nesting"/> <property name="param-single-max" value="4" type="String" description="Maximum allowed depth of lambda-single-expression nesting"/> <property name="xpath"> <value><![CDATA[ //LambdaExpression[Block][count(ancestor::LambdaExpression/Block) > number($param-block-max)] | //LambdaExpression[*[@Expression=true()]][count(ancestor::LambdaExpression) > number($param-single-max)] ]]></value> </property> </properties> <example> <![CDATA[ class Foo { public Object singleExpressionLambdas(String a, String b) { return detect(a) .orElseGet(() -> single0(b) .orElseGet(() -> single1(b) // single nesting level 1 .orElseGet(() -> single2(b)// 2 .orElseGet(() -> single3(b)// 3 .orElseGet(() -> singlet4(b)// 4 .orElseGet(() -> single5(b)// 5 // bad .orElseGet(() -> single6(b))))))));// 6 // bad } public Object blockLambdas(String a, String b) { return detect(a) .orElseGet(() -> single0(b) .orElseGet(() -> {block0(b) // block nesting level 0 .orElseGet(() -> {block1(b)// 1 .orElseGet(() -> {block2(b)// 2 // bad .orElseGet(() -> single4(b)// single nesting level 4 // good .orElseGet(() -> single5(b)));});});}));// single nesting level 5, bad } } ]]> </example> </rule> <rule name="LimitStatementsInLambdas" language="java" message="Avoid many statements in lambda expressions." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#isr03"> <description>Problem: lambda expressions with many statements are hard to understand and maintain. Solution: extract the lambda expression code block into one or more well-named separate method(s). (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="brain-overload,jpinpoint-rule,replaces-sonar-rule" type="String" description="classification"/> <property name="param-max" value="5" type="String" description="Maximum number of allowed non-setter statements"/> <property name="xpath"> <value><![CDATA[ (: number of statements should be limited, setters are ignored. Chained methods are one statement :) //LambdaExpression/Block[count(.//*[pmd-java:nodeIs('Statement')]) - count(.//EmptyStatement) - count(.//Block) - count(.//ExpressionStatement/MethodCall[starts-with(@MethodName,'set')]) > number($param-max)] ]]></value> </property> </properties> <example> <![CDATA[ class Foo { void bar(List additionals) { additionals.forEach(entity -> { if (entity.getKNr() != null) { action.withActions( new Action().withFoId(actionFoId.incrementAndGet()) .withEntityPlanFoId(1) .withEntity(mapLogicType(true)) ); } }); } void baz(List additionals) { additionals.forEach(entity -> { // bad, too many statements (5) if (entity.getKNr() != null) { action.withActions( new Action().withFoId(actionFoId.incrementAndGet()) .withEntityPlanFoId(1) .withEntity(mapLogicType(true)) ); } else { action.withActions(new Action().withFoId(0)); } }); } } ]]> </example> </rule> <rule name="LimitWildcardStaticImports" language="java" message="The number of static imports with a wildcard should be limited" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#isr01"> <description>Importing a class statically allows you to use its public static members easily: without qualifying them with the class name. Problem: if you import too many classes statically, your code can become confusing and difficult to maintain. The default maximum = 3. Solution: Import class members individually. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="confusing,jpinpoint-rule,pitfall,replaces-sonar-rule" type="String" description="classification"/> <property name="param-max" value="3" type="String" description="Maximum number of allowed static imports with wildcard"/> <property name="xpath"> <value><![CDATA[ //ImportDeclaration[@Static=true() and @ImportOnDemand=true()][position() > number($param-max)] ]]> </value> </property> </properties> <example> <![CDATA[ // With the default threshold value: 3 import static java.lang.Math.*; import static java.util.Collections.*; import static com.co.division.Constants.*; import static com.co.department.Constants.*; // bad import static com.co.corporate.Constants.NUTS; import static com.co.corporate.Constants.CRAZY; import static com.co.corporate.Constants.FOOLISH; import static com.co.corporate.Constants.AWESOME; // good import static com.co.corporate.Constants.GREAT; // good ]]> </example> </rule> <rule name="UnresolvedType" language="java" message="A type cannot be resolved. Make sure all sources are compiled and annotations processed." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md"> <description>Problem: When analyzing code, PMD could not resolve a used type (class, annotation or other type). Several rules make use of type information and assume it is available. If not, those rules typically fail to work correctly and will for instance show false positives. Solution: Make sure all types can be resolved: compile all source code, process all annotations (like lombok), compile generated code, make all dependencies available on the classpath, etc. Note: this rule is still somewhat experimental and may show false positives. (jpinpoint-rules)</description> <priority>5</priority> <properties> <property name="tags" value="confusing,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: variable access for unknown variable type :) (//VariableAccess [not(pmd-java:typeIs('java.lang.Object'))] [not(pmd-java:typeIs('byte'))][not(pmd-java:typeIs('short'))] [not(pmd-java:typeIs('int'))][not(pmd-java:typeIs('long'))] [not(pmd-java:typeIs('float'))][not(pmd-java:typeIs('double'))] [not(pmd-java:typeIs('boolean'))][not(pmd-java:typeIs('char'))] (: pmd seems not (always) able to resolve type of lambda params :) [not(@Name = ancestor::LambdaExpression//LambdaParameter/VariableId/@Name)] | (: the unresolved/ambiguous type names, except @Slf4j which pmd cannot resolve :) //AmbiguousName) [not((ancestor::ClassDeclaration|ancestor::EnumDeclaration|ancestor::RecordDeclaration)/ModifierList/Annotation[pmd-java:typeIs('lombok.extern.slf4j.Slf4j')])] ]]></value> </property> </properties> <example> <![CDATA[ public class FooBad { OutputStream bad(Path path) throws IOException { return Files.newOutputStream(path); // bad - 'Files' and 'path' unknown types } } import java.nio.file.*; public class FooGood { OutputStream good(Path path) throws IOException { return Files.newOutputStream(path); // good - 'Files' and 'path' types known because of import, and bytecode on classpath } } ]]> </example> </rule> <!-- END Included file 'common_std.xml' --> <!-- BEGIN Included file 'concurrent.xml' --> <rule name="AvoidCommonPoolForBlockingCalls" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Avoid the ForkJoinPool::commonPool used in parallelStream for blocking calls." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia09"> <description>Problem: Blocking calls, for instance remote calls, may exhaust the common pool for some time thereby blocking all other use of the common pool. In addition, nested use of the common pool can lead to deadlock. Do not use the common pool for blocking calls. The parallelStream() call uses the common pool. Solution: Use a dedicated thread pool with enough threads to get proper parallelism. The number of threads in the common pool is equal to the number of CPUs - 1 and meant to utilize all of them. It assumes CPU intensive non-blocking processing of in-memory data. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading,performance,unpredictable" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: assumption: if import of web client / http client is present, parallelStreaming is for remote calls :) //ImportDeclaration[@PackageName=('org.springframework.web.client','org.apache.commons.httpclient')] /..//MethodCall[ pmd-java:matchesSig('java.util.Collection#parallelStream()') and not(ancestor::ExpressionStatement//MethodCall[pmd-java:matchesSig('java.util.concurrent.ExecutorService#submit(_)')]) ] ]]></value> </property> </properties> <example> <![CDATA[ import java.util.concurrent.*; import java.util.stream.*; import java.util.*; import org.springframework.web.client.RestTemplate; public class Foo { final List<String> list = new ArrayList(); final ForkJoinPool myFjPool = new ForkJoinPool(10); final ExecutorService myExePool = Executors.newFixedThreadPool(10); void bad1() { list.parallelStream().forEach(elem -> storeDataRemoteCall(elem)); // bad } void good1() { CompletableFuture[] futures = list.stream().map(elem -> CompletableFuture.supplyAsync(() -> storeDataRemoteCall(elem), myExePool)) .toArray(CompletableFuture[]::new); CompletableFuture.allOf(futures).get(10, TimeUnit.MILLISECONDS)); } void good2() throws ExecutionException, InterruptedException { myFjPool.submit(() -> list.parallelStream().forEach(elem -> storeDataRemoteCall(elem)) ).get(); } String storeDataRemoteCall(String elem) { // do remote call, blocking. We don't use the returned value. RestTemplate tmpl; return ""; } } ]]> </example> </rule> <rule name="AvoidCommonPoolForFutureAsync" message="Avoid using the common thread pool for future.supplyAsync, use a separate pool." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia08"> <description> Problem: Future.supplyAsync is typically used for remote calls. By default, it uses the common pool. The number of threads in the common pool is equal to the number of CPU's, which is suitable for in-memory processing. For I/O, however, this number is typically not suitable because most time is spent waiting for the response and not in CPU. The common pool must not be used for blocking calls. Solution: A separate, properly sized, pool of threads (an Executor) should be used for the async calls. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading,performance,pitfall,unpredictable" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[pmd-java:matchesSig('java.util.concurrent.CompletableFuture#supplyAsync(_)')] ]]></value> </property> </properties> <example> <![CDATA[ public class Foo { private final ExecutorService asyncPool = Executors.newFixedThreadPool(8); void bad() { CompletableFuture<Pair<String, Boolean>>[] futures = accounts.stream() .map(account -> CompletableFuture.supplyAsync(() -> isAccountBlocked(account))) // bad .toArray(CompletableFuture[]::new); } void good() { CompletableFuture<Pair<String, Boolean>>[] futures = accounts.stream() .map(account -> CompletableFuture.supplyAsync(() -> isAccountBlocked(account), asyncPool)) // good .toArray(CompletableFuture[]::new); } } ]]> </example> </rule> <rule name="AvoidCompletionServiceTake" message="Avoid completionService.take, use poll" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md"> <description> Problem: take() stalls indefinitely in case of hanging threads and consumes a thread. Solution: use poll() with a timeout value and handle the timeout. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading,performance,pitfall" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //MethodCall[pmd-java:matchesSig('java.util.concurrent.CompletionService#take()')] ]]> </value> </property> </properties> <example><![CDATA[ public static <T> void collectAllCollectionReplyFromThreads(CompletionService<List<T>> completionService) { try { Future<List<T>> futureLocal = completionService.take(); // bad Future<List<T>> futuresGood = completionService.poll(3, TimeUnit.SECONDS); // good responseCollector.addAll(futuresGood.get(10, TimeUnit.SECONDS)); // good } catch (InterruptedException | ExecutionException e) { LOGGER.error("Error in Thread : {}", e); } catch (TimeoutException e) { LOGGER.error("Timeout in Thread : {}", e); } } ]]></example> </rule> <rule name="AvoidExecutorInvokeWithoutTimeout" message="Avoid using ExecutorService::invokeAll or invokeAny without a timeout." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia15"> <description>Problem: Stalls indefinitely in case of stalled Callable(s) and consumes threads. Solution: Provide a timeout to the invokeAll/invokeAny method and handle the timeout. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading,performance,pitfall" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[pmd-java:matchesSig('java.util.concurrent.ExecutorService#invokeAll(_)') or pmd-java:matchesSig('java.util.concurrent.ExecutorService#invokeAny(_)')] ]]></value> </property> </properties> <example><![CDATA[ import java.util.concurrent.*; class Foo { private List<Future<ServiceResult>> executeTasksBad(Collection<Callable<ServiceResult>> tasks, ExecutorService executor) throws Exception { return executor.invokeAll(tasks); // bad, no timeout } private List<Future<ServiceResult>> executeTasksGood(Collection<Callable<ServiceResult>> tasks, ExecutorService executor) throws Exception { return executor.invokeAll(tasks, OUR_TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS); // good } } ]]></example> </rule> <rule name="AvoidFutureGetWithoutTimeout" message="Avoid future.get without timeout" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md"> <description> Problem: Stalls indefinitely in case of hanging threads and consumes a thread. Solution: Provide a timeout value and handle the timeout. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading,performance" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[pmd-java:matchesSig('java.util.concurrent.Future#get()')] ]]></value> </property> </properties> <example><![CDATA[ public static String bad(CompletableFuture<String> complFuture) throws Exception { return complFuture.get(); // bad } public static String good(CompletableFuture<String> complFuture) throws Exception { return complFuture.get(10, TimeUnit.SECONDS); // good } ]]></example> </rule> <rule name="AvoidFutureJoinWithoutTimeout" message="Avoid future.join without timeout" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia07"> <description> Problem: Stalls indefinitely in case of hanging thread(s) and consumes a thread. Solution: Provide a timeout before the join and handle the timeout. For example a future.get(timeout, unit), a orTimeout() or a completeOnTimeout(). You may want to use CompletableFuture.allOf() too. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading,performance,pitfall" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ (: future.join method without timeout or without timeout in preceding methods :) //MethodDeclaration/Block/*[not(preceding-sibling::*[descendant-or-self::MethodCall[ pmd-java:matchesSig('java.util.concurrent.Future#get(long,java.util.concurrent.TimeUnit)') or pmd-java:matchesSig('java.util.concurrent.Future#orTimeout(long,java.util.concurrent.TimeUnit)') or pmd-java:matchesSig('java.util.concurrent.Future#completeOnTimeout(_,long,java.util.concurrent.TimeUnit)') ]] )]//(MethodCall|MethodReference)[ (pmd-java:matchesSig('java.util.concurrent.Future#join()') or (self::MethodReference and @MethodName='join')) (: no orTimeout(_,_) call in same chained expression :) and not (ancestor::Block//(ExpressionStatement|LocalVariableDeclaration)//MethodCall[pmd-java:matchesSig('java.util.concurrent.Future#orTimeout(long,java.util.concurrent.TimeUnit)')]) ] ]]></value> </property> </properties> <example> <![CDATA[ private List<Order> getOrdersBad(List<CompletableFuture<Order>> getOrdersFutures) { List<Order> orders = getOrdersFutures.stream() .map(CompletableFuture::join) // bad, NO timeout provided above .collect(Collectors.toList()); return orders; } private List<Order> getOrdersGood(List<CompletableFuture<Order>> getOrdersFutures) { // added to deal with timeout CompletableFuture<Void> allFuturesResult = CompletableFuture.allOf(getOrdersFutures.toArray(new CompletableFuture[getOrdersFutures.size()])); try { allFuturesResult.get(5L, TimeUnit.SECONDS); // good } catch (Exception e) { // should make explicit Exceptions //log error } List<Order> orders = getOrdersFutures.stream() .filter(future -> future.isDone() && !future.isCompletedExceptionally()) // keep only the ones completed - - added to deal with timeout .map(CompletableFuture::join) // good, timeout provided above .collect(Collectors.toList()); return orders; } ]]> </example> </rule> <rule name="AvoidIncrementOrDecrementForVolatileField" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Avoid ++, - -, +=, -= for volatile fields" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc09"> <description>A compound statement like i++, i- -, i += 1 or i -= 1 may seem one statement and thread-safe for a volatile field. Problem: The operation is actually two separate statements executed non-atomically and therefore not thread-safe. Solution: Guard the field properly with synchronized or use atomics like AtomicInteger. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //( AssignmentExpression[@Operator='+=' or @Operator='-=']/*[1] | UnaryExpression[@Operator='++' or @Operator='--']/(VariableAccess|FieldAccess) )[ @Name = ancestor::ClassDeclaration//FieldDeclaration[pmd-java:modifiers()='volatile']/VariableDeclarator/VariableId/@Name ] ]]> </value> </property> </properties> <example> <![CDATA[ public class Foo { private volatile int i; private volatile String str = ""; public int bad() { i++; // bad - -i; // bad i += 1; // bad str += "a"; // bad } public int good(int v) { i = 5; // good i = v + 5; // good str = "other"; // good } } ]]> </example> </rule> <rule name="AvoidMDCInReactor" message="Avoid using MDC with Reactor." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia13"> <description>Problem: Mapped Diagnostic Context (MDC) of logging frameworks uses ThreadLocals to store things like traceIds from headers, userId, correlationId. Reactive programming uses multiple threads to handle a request, and one thread can deal with asynchronous steps of many requests. Therefore, MDC is tricky to use in reactive context and may take much processing time to propagate, likely so for much data in the MDC. Solution: Propagate by use of deferContextual and use directly from the Context only when and where needed. Avoid MDC propagation mechanisms. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,multi-threading,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[ pmd-java:matchesSig('org.slf4j.MDC#setContextMap(_)') and ( ancestor::MethodDeclaration/ClassType[ pmd-java:typeIs('reactor.core.publisher.Flux') or pmd-java:typeIs('reactor.core.publisher.Mono') ] or ancestor::MethodDeclaration/FormalParameters/FormalParameter[ pmd-java:typeIs('reactor.util.context.Context') ] ) ] ]]></value> </property> </properties> <example> <![CDATA[ import reactor.core.publisher.*; import reactor.util.context.Context; import org.slf4j.MDC; import net.logstash.logback.argument.StructuredArguments; class FooBad { public Flux<ServerResponse> doIt(Map<String,String> contextMap) { return Flux.fromIterable(service.doWork()) .doOnNext(s -> MDC.setContextMap(contextMap)) .doOnNext( response -> { log.info("your log"); // includes trace ids from MDC, put in a ServletFilter, specified by log configuration }); } } class FooGood { public Flux<ServerResponse> doIt() { // using deferContextual gives access to the read-only ContextView created in the ContextFilter return Flux.deferContextual(contextView -> service.doWork() .doOnNext(response -> { log.info("your log", StructuredArguments.entries((Map)contextView)); })); } } ]]> </example> </rule> <rule name="AvoidMutableCollectionInParallelStreamForEach" message="Avoid using a mutable collection in a parallel stream forEach, it is a concurrency bug." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia15"> <description>Problem: If a thread-unsafe collection is mutated in a parallelStream, this means that multiple threads access an unguarded shared variable which can cause data corruption and visibility problems: a concurrency bug. Solution: Using a thread-safe collection would fix the bug, however, the proper solution is to only use forEach for reporting/logging and let streams not have side effects. Moreover, parallelization is only faster in the exceptional case with large collections and much CPU work per element. Always measure if really faster.(jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading,performance,pitfall" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: should match java.lang.Stream#forEach(java.util.function.Consumer) but also others like java.util.stream.IntStream#forEach(java.util.function.IntConsumer) :) //MethodCall[pmd-java:matchesSig('java.util.stream.BaseStream#forEach(_)')][.//MethodCall[@MethodName='parallel' or @MethodName='parallelStream']] [ArgumentList[ .//(LambdaExpression|MethodReference)[ (: a constructor of a thread-unsafe collection/map :) .//VariableAccess/@Name = (ancestor::MethodDeclaration//LocalVariableDeclaration|ancestor::ClassBody/FieldDeclaration)/VariableDeclarator[ ConstructorCall[pmd-java:typeIs('java.util.ArrayList')] or ConstructorCall[pmd-java:typeIs('java.util.HashMap')] or ConstructorCall[pmd-java:typeIs('java.util.HashSet')] or ConstructorCall[pmd-java:typeIs('java.util.LinkedList')] or ConstructorCall[pmd-java:typeIs('java.util.TreeMap')] or ConstructorCall[pmd-java:typeIs('java.util.TreeSet')] or ConstructorCall[pmd-java:typeIs('java.util.EnumMap')] or ConstructorCall[pmd-java:typeIs('java.util.ArrayDeque')] or ConstructorCall[pmd-java:typeIs('java.util.PriorityQueue')] or ArrayAllocation ]/(VariableId|VariableAccess)/@Name] ] ] ]]></value> </property> </properties> <example><![CDATA[ class AvoidMutableCollectionInParallelStreamForEach { static List<String> names = List.of("Dory", "Gill", "Bruce", "Nemo", "Darla", "Marlin", "Jacques"); List<String> inUppercaseField = new ArrayList<>(); public static void main(String[] args) { List<String> inUppercase = new ArrayList<>(); names.parallelStream().map(String::toUpperCase).forEach(name -> inUppercase.add(name)); // bad System.out.println(names.size()); // 7 System.out.println(inUppercase.size()); // sometimes 6! As Venkat's demo! } public void noBug() { List<String> inUppercase = Collections.synchronizedList(new ArrayList<>()); // thread-safe collection names.parallelStream().map(String::toUpperCase).forEach(name -> inUppercase.add(name)); } public void good() { // pure functional form, proper solution. Note parallel may be faster, yet only with large collections. Always measure! List<String> inUppercase = names.parallelStream().map(String::toUpperCase).toList(); // java 12+ } ]]></example> </rule> <rule name="AvoidMutableStaticFields" message="Avoid non-final or mutable static fields. Make final immutable or access thread-safely AND use @GuardedBy." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc08"> <description> Problem: Multiple threads typically access static fields. Unguarded assignment to a mutable or non-final static field is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and @GuardedBy, use volatile or a suitable concurrent collection type. Consider lock contention. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: -- GuardedBy annotations are matched on SimpleName, so it matches all known GuardedBy annotations :) (: -- net.jcip.annotations.GuardedBy, javax.annotation.concurrent.GuardedBy, com.google.errorprone.annotations.concurrent.GuardedBy :) (: static field, non-final, non-volatile, non-guarded by :) //FieldDeclaration[ pmd-java:modifiers() = ('static') and not(pmd-java:modifiers() = ('final','volatile')) and not(./ModifierList/Annotation[@SimpleName='GuardedBy']) ] , (: static field, non-guarded, some often used known mutable types, declaration side :) //FieldDeclaration[ pmd-java:modifiers() = ('static') and (./ClassType[(pmd-java:typeIs('java.util.Date') or pmd-java:typeIs('java.lang.StringBuilder') or pmd-java:typeIs('java.lang.StringBuffer') or pmd-java:typeIs('java.net.URL')) or pmd-java:typeIs('java.io.File')] or ./VariableDeclarator/ArrayInitializer[@Length > 0]) and not(./ModifierList/Annotation[@SimpleName='GuardedBy']) ] , (: static field, non-guarded, some often used known collection/array types, allocation side:) //FieldDeclaration[ pmd-java:modifiers() = ('static') and not(./ModifierList/Annotation[@SimpleName='GuardedBy']) and ( .//ConstructorCall/ClassType[ (pmd-java:typeIs('java.util.ArrayList') and not(ancestor::MethodCall[pmd-java:matchesSig('java.util.Collections#unmodifiableList(_*)')]) ) or ((pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.EnumMap')) and not(ancestor::MethodCall[pmd-java:matchesSig('java.util.Collections#unmodifiableMap(_*)')])) or (pmd-java:typeIs('java.util.HashSet') and not(ancestor::MethodCall[pmd-java:matchesSig('java.util.Collections#unmodifiableSet(_*)')])) ] or .//ArrayInitializer[@Length > 0] or .//ArrayAllocation[.//ArrayDimExpr/NumericLiteral[@ValueAsInt > 0]] or .//MethodCall[pmd-java:matchesSig('java.util.Arrays#asList(_*)') and not(ancestor::MethodCall[pmd-java:matchesSig('java.util.Collections#unmodifiableList(_*)')])] or .//MethodCall/TypeExpression[pmd-java:typeIs('java.util.EnumSet') and not(ancestor::MethodCall[pmd-java:matchesSig('java.util.Collections#unmodifiableSet(_*)')])] ) ], (: static-block allocations of non-empty arrays :) //Initializer//ArrayAllocation[ArrayType/ArrayDimensions[@Size > 0]/ArrayDimExpr/NumericLiteral[@ValueAsInt > 0] or exists(.//ArrayDimExpr/VariableAccess/@Name) or exists(.//ArrayInitializer/*)] , (: static-block allocations of known mutable types :) //Initializer//ExpressionStatement//ConstructorCall[ ( (pmd-java:typeIs('java.util.ArrayList') and not(ancestor::MethodCall[pmd-java:matchesSig('java.util.Collections#unmodifiableList(_*)')]) ) or ((pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.EnumMap')) and not(ancestor::MethodCall[pmd-java:matchesSig('java.util.Collections#unmodifiableMap(_*)')])) or (pmd-java:typeIs('java.util.HashSet') and not(ancestor::MethodCall[pmd-java:matchesSig('java.util.Collections#unmodifiableSet(_*)')])) ) and not(../VariableAccess/@Name = ancestor::ClassDeclaration//FieldDeclaration[count(ModifierList/Annotation[@SimpleName='GuardedBy']) > 0]/VariableDeclarator/VariableId/@Name) ] ]]></value> </property> </properties> <example><![CDATA[ enum Bad { VAL1; static final Map<String, Bad> STRING_TO_ENUM = new HashMap<>(); // bad } enum Good { VAL1; static final Map<String, Good> STRING_TO_ENUM; // good static { Map<String, Good> map = new HashMap<>(); STRING_TO_ENUM = Collections.unmodifiableMap(map); } } ]]></example> </rule> <rule name="AvoidNonPrivateFieldsInSharedObjects" message="Avoid non-private objects shared among threads. Make fields private to ensure thread-safety." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc11"> <description> Problem: A singleton, or more general: an object shared among threads, has a field that is not private. These field can possibly be modified from other classes. Solution: Make the fields private. Note: fields are excluded when annotated with: @Autowired/@Value/@Inject. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ (: known assumed singleton types by annotation :) //ClassDeclaration[ ( pmd-java:hasAnnotation('org.springframework.stereotype.Service') or pmd-java:hasAnnotation('org.springframework.stereotype.Component') or pmd-java:hasAnnotation('org.springframework.stereotype.Controller') or pmd-java:hasAnnotation('org.springframework.stereotype.Repository') or pmd-java:hasAnnotation('org.springframework.web.bind.annotation.RestController') or (pmd-java:hasAnnotation('javax.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('javax.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('javax.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) or (pmd-java:hasAnnotation('jakarta.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) ) (: not shared when request or prototype scope :) and not(pmd-java:hasAnnotation('org.springframework.context.annotation.Scope') and .//AnnotationMemberList/MemberValuePair[ StringLiteral[contains(@Image,'request') or contains(@Image,'prototype')] or FieldAccess[@Name='SCOPE_REQUEST' or @Name='SCOPE_PROTOTYPE']/TypeExpression[pmd-java:typeIs('org.springframework.beans.factory.config.ConfigurableBeanFactory')]] ) and not(pmd-java:hasAnnotation('org.springframework.context.annotation.RequestScope')) (: if @NotThreadSafe no checking :) and not(pmd-java:hasAnnotation('net.jcip.annotations.NotThreadSafe')) ] (: non-static, non-final and non-private fields :) //FieldDeclaration[ not(pmd-java:modifiers()=('private','final')) (: field not injection framework annotated :) and not( pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) ) (: no innerclass, no static class :) and not(ancestor::ClassDeclaration[@Nested=true() or pmd-java:modifiers()='static']) ] ]]> </value> </property> </properties> <example><![CDATA[ @Controller public class RootController { List baskets; // bad, not private protected List baskets2; // bad, not private public List baskets3; // bad, not private final List baskets4; // good, final static List baskets5; // bad, static not private static final List baskets6; // good, static private List baskets7; // good } ]]></example> </rule> <rule name="AvoidParallelFlux" message="Avoid dividing the data and parallel processing because this is typically slower." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia12"> <description>Problem: With Reactor Flux.parallel.runOn, the data is divided on a number of 'rails' matching the number of CPU cores. This is only useful in case much CPU processing is performed: if the sequential form takes more than 0,1 ms of CPU time. With remote calls this is usually not the case. In addition, it introduces more complexity with risk of failures. Solution: Remove parallel().runOn. Unless the CPU work takes more than 0,1 ms in sequential form and proves to be faster with parallelization. So only for large collections and much CPU processing. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="bad-practice,cpu,jpinpoint-rule,multi-threading,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[pmd-java:matchesSig('reactor.core.publisher.Flux#parallel(_*)') and ./MethodCall[pmd-java:matchesSig('reactor.core.publisher.Flux#fromIterable(_)')]] ]]></value> </property> </properties> <example><![CDATA[ import reactor.core.publisher.*; class FooBad { public Flux<Account> getResponseAccounts(List<AccountKey> accountKeys, List<FieldName> requestedFields) { return Flux.fromIterable(accountKeys) .parallel(schedulerProperties.getParallelism()) //bad .runOn(scheduler) .flatMap(accountKey -> constructAccountDetails(accountKey, requestedFields)) .sequential(); } } class FooGood { public Flux<Account> getResponseAccounts(List<AccountKey> accountKeys, List<FieldName> requestedFields) { return Flux.fromIterable(accountKeys) .flatMap(accountKey -> constructAccountDetails(accountKey, requestedFields)); } } ]]></example> </rule> <rule name="AvoidParallelStreamWithCommonPool" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Beware: parallelStream with the common pool is rarely a good choice." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia11"> <description>Problem: Collection.parallelStream uses the common pool, with #threads = #CPUs - 1. It is designed to distribute much CPU work over the cores. It is not designed for remote calls or other blocking calls. In addition, parallelizing has CPU overhead and concurrency risks. It should only be used for much pure CPU processing, e.g. on collections with > 10.000 elements. Solution: For remote/blocking calls: Use a dedicated thread pool with enough threads to get proper parallelism independent of the number of cores. For pure CPU processing: use ordinary sequential streaming unless the work takes more than about 0,1 ms in sequential form and proves to be faster with parallelization by measuring. So only for large collections and much processing without having to wait. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,multi-threading,performance,sustainability-medium,unpredictable" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[ ( pmd-java:matchesSig('java.util.Collection#parallelStream()') or pmd-java:matchesSig('java.util.stream.BaseStream#parallel()') ) and not(ancestor::ExpressionStatement//MethodCall[pmd-java:matchesSig('java.util.concurrent.ExecutorService#submit(_)')]) ] ]]> </value> </property> </properties> <example> <![CDATA[ import java.util.*; import java.util.concurrent.*; import java.util.stream.*; public class Foo { final Map<String, String> map = new HashMap(); final List<String> list = new ArrayList(); final List<String> hugeList = new ArrayList(); //1000+ elements final ForkJoinPool myFjPool = new ForkJoinPool(10); final ExecutorService myExePool = Executors.newFixedThreadPool(10); void bad1() { list.parallelStream().forEach(elem -> someCall(elem)); // bad } void bad2() { map.entrySet().parallelStream().forEach(entry -> someCall(entry.getValue())); // bad } void exceptionalProperUse() { hugeList.parallelStream().forEach(elem -> heavyCalculations(elem)); // flagged but may be good, should suppress when proven to be faster than sequential form } void good1() { CompletableFuture[] futures = list.stream().map(elem -> CompletableFuture.supplyAsync(() -> someCall(elem), myExePool)) .toArray(CompletableFuture[]::new); CompletableFuture.allOf(futures).get(3, TimeUnit.SECONDS); } void good2() throws ExecutionException, InterruptedException { myFjPool.submit(() -> list.parallelStream().forEach(elem -> someCall(elem)) ).get(); } String someCall(String elem) { // do some call, don't know if remote or blocking. We don't use the returned value. return ""; } String heavyCalculations(String elem) { // calculate a lot return ""; } } ]]> </example> </rule> <rule name="AvoidReactorOnEachOperator" message="Avoid using Reactor Hooks.onEachOperator." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia14"> <description>Problem: Using Reactor Hooks.onEachOperator means executing the code on every operator in the Reactor flow, for every element. This typically means much processing time. Solution: Just do processing when and where actually needed. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[pmd-java:matchesSig('reactor.core.publisher.Hooks#onEachOperator(_,_)')] ]]></value> </property> </properties> <example> <![CDATA[ import reactor.core.publisher.*; @Configuration public class FooBad { private String MDC_CONTEXT_REACTOR_KEY = "CONTEXT"; @PostConstruct private void contextOperatorHook() { Hooks.onEachOperator(MDC_CONTEXT_REACTOR_KEY, Operators.lift((scannable, coreSubscriber) -> new MdcContextLifter<>(coreSubscriber)) // bad ); } } ]]> </example> </rule> <rule name="AvoidStaticXmlFactories" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Avoid static XML Factories" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#IUOXAR09"> <description>An XML Factory like DocumentBuilderFactory, TransformerFactory, MessageFactory is used as static field. Problem: These factory objects are typically not thread-safe and rather expensive to create because of class loading. Solution: Create the Factories as local variables and use command line arguments to specify the implementation class. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading,pitfall" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassDeclaration//FieldDeclaration[ pmd-java:modifiers()='static' and ClassType[ pmd-java:typeIs('javax.xml.parsers.DocumentBuilderFactory') or pmd-java:typeIs('javax.xml.parsers.SAXParserFactory') or pmd-java:typeIs('javax.xml.stream.XMLInputFactory') or pmd-java:typeIs('javax.xml.stream.XMLOutputFactory') or pmd-java:typeIs('javax.xml.stream.XMLEventFactory') or pmd-java:typeIs('javax.xml.transform.TransformerFactory') or pmd-java:typeIs('javax.xml.validation.SchemaFactory') or pmd-java:typeIs('javax.xml.datatype.DatatypeFactory') or pmd-java:typeIs('javax.xml.soap.MessageFactory') ] ] ]]></value> </property> </properties> <example> <![CDATA[ public class Bad { private static final DocumentBuilderFactory DB_FACTORY = DocumentBuilderFactory.newInstance(); // shared, bad } public class Good { public build() { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); // local var, good // use dbFactory } } ]]> </example> </rule> <rule name="AvoidThreadUnsafeJaxbUsage" message="A JAXB Marshaller, Unmarshaller or Validator is used in a thread-unsafe way." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#IUOXAR07"> <description>Problem: JAXB Marshaller, Unmarshaller and Validator are not thread-safe. Solution: Create a new instance every time you need to marshall, unmarshall or validate a document. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //FieldDeclaration[ClassType[pmd-java:typeIs('javax.xml.bind.Marshaller') or pmd-java:typeIs('javax.xml.bind.Unmarshaller') or pmd-java:typeIs('javax.xml.bind.Validator') or pmd-java:typeIs('javax.xml.validation.Validator')] ] ]]></value> </property> </properties> <example><![CDATA[ class Exporter { private javax.xml.bind.Unmarshaller sharedUnmarshaller; // bad private javax.xml.bind.Validator sharedValidator; // bad public Thing doSomething(File xml) { Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); // good, create one each time return (Thing) unmarshaller.unmarshal(xml); } } ]]></example> </rule> <rule name="AvoidUnguardedAssignmentToNonFinalFieldsInObjectsUsingSynchronized" message="Avoid unguarded assignments to non-final fields in objects using synchronized. Access thread-safely AND use @GuardedBy." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc07"> <description> Problem: Multiple threads typically access fields of an object using synchronized. Unguarded assignment to a non-final field is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and jcip @GuardedBy or use volatile. Notes 1. In case you are sure the class is used in single threaded context only, remove current use of synchronized and annotate the class with @NotThreadSafe to make this explicit. 2. Use package-private and @VisibleForTesting for methods (e.g. setters) used for JUnit only. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: if @NotThreadSafe no checking :) //ClassDeclaration[ not (pmd-java:hasAnnotation('net.jcip.annotations.NotThreadSafe')) (: not in static inner class :) and not (ancestor::ClassDeclaration[@Nested=true() and @Static=true()]) and (.//SynchronizedStatement or .//MethodDeclaration[pmd-java:modifiers()='synchronized']) ] (: -- GuardedBy annotations are matched on SimpleName, so it matches all known GuardedBy annotations :) (: -- net.jcip.annotations.GuardedBy, javax.annotation.concurrent.GuardedBy, com.google.errorprone.annotations.concurrent.GuardedBy :) //AssignmentExpression/*[1][ (: is a field of class :) @Name = ancestor::ClassDeclaration//FieldDeclaration/VariableDeclarator/VariableId/@Name (: but not final or volatile or GuardedBy :) and not(@Name = ancestor::ClassDeclaration//FieldDeclaration[ pmd-java:modifiers()=('final','volatile') or ModifierList/Annotation[@SimpleName='GuardedBy'] ]/VariableDeclarator/VariableId/@Name) (: field not on accessor method with assignment level annotated with framework annotation :) and not( ancestor::MethodDeclaration[ pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct')) or pmd-java:hasAnnotation('org.springframework.batch.core.annotation.BeforeStep') or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) ] (: field not assigned in non-public accessor method annotated with VisibleForTesting :) or (ancestor::MethodDeclaration[pmd-java:hasAnnotation('com.google.common.annotations.VisibleForTesting') and not(@Visibility='public')]) (: annotation is not enough? :) or ancestor::MethodDeclaration[@Name='afterPropertiesSet'] ) ] ]]></value> </property> </properties> <example><![CDATA[ public class Bad { private static Map staticMapField = new HashMap(); private Date date = new Date(); public Date getDate() { return date; } public void setDate(Date d) { date = d; // bad - synchronized used in class } public static synchronized Map getStaticMapField() { return staticMapField; } public static void setStaticMapField(Map smf) { staticMapField = smf; // bad - synchronized use in class } } @NotThreadSafe public class Good { private static Map staticMapField = new HashMap(); private Date date = new Date(); public Date getDate() { return date; } public void setDate(Date d) { date = d; // good - no synchronized in class (marked as @NotThreadSafe) } public static Map getStaticMapField() { return staticMapField; } public static void setStaticMapField(Map smf) { staticMapField = smf; // good - no synchronized in class (marked as @NotThreadSafe) } } ]]></example> </rule> <rule name="AvoidUnguardedAssignmentToNonFinalFieldsInSharedObjects" message="Avoid unguarded assignments to non-final fields in objects shared among threads. Access thread-safely AND use @GuardedBy." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc07"> <description> Problem: Multiple threads typically access fields of a singleton or may access fields in session scoped objects. Unguarded assignment to a non-final field is thread-unsafe and may cause corruption or visibility problems. It may also unintentionally mix-up session data. Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and jcip @GuardedBy or use volatile. Notes 1. For autowiring/injection, the assignment of the reference is thread safe, so final is not required. The unmodifiable/thread-safe requirement for that field still holds. Also make sure no other thread-unsafe assignment is made to that field. 2. In case you are sure the Component is used in single threaded context only (e.g. a Tasklet), annotate the class with @NotThreadSafe to make this explicit. 3. Use package-private and @VisibleForTesting for methods (e.g. setters) used for JUnit only. 4. Use synchronized for accessors to inherited fields, or better: make field private and use proper accessors on base class level using @GuardedBy. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="data-mix-up,jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: annotations that make singleton like components in Spring and EJB :) //ClassDeclaration[ ( pmd-java:hasAnnotation('org.springframework.stereotype.Service') or pmd-java:hasAnnotation('org.springframework.stereotype.Component') or pmd-java:hasAnnotation('org.springframework.stereotype.Controller') or pmd-java:hasAnnotation('org.springframework.stereotype.Repository') or pmd-java:hasAnnotation('org.springframework.web.bind.annotation.RestController') or (pmd-java:hasAnnotation('javax.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('javax.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('javax.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) or (pmd-java:hasAnnotation('jakarta.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) ) (: no @RequestScope annotation :) and not (pmd-java:hasAnnotation('org.springframework.context.annotation.Scope') and .//AnnotationMemberList/MemberValuePair[ StringLiteral[contains(@Image,'request') or contains(@Image,'prototype')] or FieldAccess[@Name='SCOPE_REQUEST' or @Name='SCOPE_PROTOTYPE']/TypeExpression[pmd-java:typeIs('org.springframework.beans.factory.config.ConfigurableBeanFactory')]] ) (: no request or prototype @Scope, literal or via constant :) and not(pmd-java:hasAnnotation('org.springframework.web.context.annotation.RequestScope')) (: if @NotThreadSafe no checking :) and not(pmd-java:hasAnnotation('net.jcip.annotations.NotThreadSafe')) (: no checking if @ConfigurationProperties and no @Setter :) and not(pmd-java:hasAnnotation('org.springframework.boot.context.properties.ConfigurationProperties')) and not(pmd-java:hasAnnotation('lombok.Setter')) ] (: assignment to a field, get left hand of assignment :) //AssignmentExpression/*[1][ ( (: -- GuardedBy annotations are matched on SimpleName, so it matches all known GuardedBy annotations :) (: -- net.jcip.annotations.GuardedBy, javax.annotation.concurrent.GuardedBy, com.google.errorprone.annotations.concurrent.GuardedBy :) (: non-final, non-volatile and non-GuardedBy fields, of only the current class :) @Name = ancestor::ClassBody[1]/FieldDeclaration[ not(pmd-java:modifiers() = ('final','volatile') or ModifierList/Annotation[@SimpleName='GuardedBy']) ]//VariableId/@Name or ( (: extends a base class :) ancestor::ClassDeclaration//ExtendsList (: and not in constructor - could be final field :) and not (ancestor::ConstructorDeclaration) and not( (: var is not a local var :) @Name = ancestor::Block//LocalVariableDeclaration//VariableId/@Name or (: var is not a param :) @Name = ancestor::MethodDeclaration//FormalParameter/VariableId/@Name or (: var is not a final, volatile or GuardedBy field of only the current class :) @Name = ancestor::ClassBody[1]/FieldDeclaration[ pmd-java:modifiers() = ('final','volatile') or ModifierList/Annotation[@SimpleName='GuardedBy'] ]//VariableId/@Name ) and not( ancestor::SynchronizedStatement or ancestor::MethodDeclaration[pmd-java:modifiers() = 'synchronized'] ) ) ) (: not in static inner class :) and not (ancestor::ClassDeclaration[@Nested=true() and @Static=true()]) (: field not on accessor method with assignment, annotated with framework annotation :) and not( ancestor::MethodDeclaration[ pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct')) or pmd-java:hasAnnotation('org.springframework.batch.core.annotation.BeforeStep') or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) ] (: field not assigned in non-public accessor method annotated with VisibleForTesting :) or (ancestor::MethodDeclaration[pmd-java:hasAnnotation('com.google.common.annotations.VisibleForTesting') and not(@Visibility='public')]) (: annotation is not enough? :) or ancestor::MethodDeclaration[@Name='afterPropertiesSet'] ) ] ]]></value> </property> </properties> <example><![CDATA[ @Service class MyService { private final LOCK = new Object(); @Value("${batchService.http.url}") private String batchUrl; @GuardedBy("LOCK") @Value("${otherService.http.url}") private String otherUrl; public void setBatchUrl(final String url) { this.batchUrl = url; // bad: unguarded accessor method } public void setOtherUrl(final String url) { synchronized(LOCK) { this.batchUrl = url; // good: synchronized accessor method } } } ]]></example> </rule> <rule name="AvoidUnguardedMutableFieldsInObjectsUsingSynchronized" message="Avoid unguarded non-final or mutable fields in objects using synchronized. Make final immutable or access thread-safely AND use @GuardedBy." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc07"> <description> Problem: Multiple threads typically access fields of an object using synchronized. If a field or its reference is mutable, access is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and jcip @GuardedBy or use volatile. Notes 1. Instances of Date, StringBuilder, URL and File are examples of mutable objects and should be avoided (or else guarded) as fields of shared objects. In case mutable fields are final and not modified after initialization (read-only) they are thread safe, however any modification to it is thread-unsafe. Since field modification is easily coded, avoid this situation. 2. Instances of classes like ArrayList, HashMap and HashSet are also mutable and should be properly wrapped with e.g. Collections.unmodifiableList after initialization (see TUTC03), or accessed thread-safely with e.g. Collections.synchronizedList or thread-safe implementations like ConcurrentHashMap. 3. For autowiring/injection, the assignment of the reference is thread safe, so final is not required. The unmodifiable/thread-safe requirement for that field still holds. Also make sure no other thread-unsafe assignment is made to that field. 4. In case you are sure the class is used in single threaded context only, annotate the class with @NotThreadSafe to make this explicit. 5. Use package private and @VisibleForTesting for methods used for JUnit only. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ (: if @NotThreadSafe no checking :) //ClassDeclaration[ not (pmd-java:hasAnnotation('net.jcip.annotations.NotThreadSafe')) (: not in static inner class :) and not (ancestor::ClassDeclaration[@Nested=true() and @Static=true()]) and (.//SynchronizedStatement or .//MethodDeclaration[pmd-java:modifiers()='synchronized']) ] //FieldDeclaration[ (: assignment to a field that is not final or volatile:) not(pmd-java:modifiers()=('final','volatile')) (: field not annotated with framework annotation or GuardedBy :) and not(pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.persistence.PersistenceContext') or pmd-java:hasAnnotation('jakarta.persistence.PersistenceContext')) or (pmd-java:hasAnnotation('javax.ejb.EJB') or pmd-java:hasAnnotation('jakarta.ejb.EJB')) or (pmd-java:hasAnnotation('javax.annotation.Resource') or pmd-java:hasAnnotation('jakarta.annotation.Resource')) or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') (: -- GuardedBy annotations are matched on SimpleName, so it matches all known GuardedBy annotations :) (: -- net.jcip.annotations.GuardedBy, javax.annotation.concurrent.GuardedBy, com.google.errorprone.annotations.concurrent.GuardedBy :) or ModifierList/Annotation[@SimpleName='GuardedBy'] ) (: field not on accessor method with assignment level annotated with framework annotation :) and not ( ./VariableDeclarator/VariableId/@Name = ancestor::ClassDeclaration//MethodDeclaration[ pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct')) or pmd-java:hasAnnotation('org.springframework.batch.core.annotation.BeforeStep') or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) ]//VariableAccess/@Name ) or ( (: or field of known mutable types including array :) (./ClassType[pmd-java:typeIs('java.util.Date') or pmd-java:typeIs('java.lang.StringBuilder') or pmd-java:typeIs('java.lang.StringBuffer') or pmd-java:typeIs('java.net.URL') or pmd-java:typeIs('java.io.File')] (: or mutable array :) or ( .//ArrayInitializer[@Length > 0] ) ) (: or in-line allocation of known mutable collection types :) or ( ./VariableDeclarator/ConstructorCall/ClassType[ pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet') ] ) (: or in-constructor allocation of known mutable collection types :) or ( ./VariableDeclarator/VariableId/@Name = ancestor::ClassDeclaration//ConstructorDeclaration//AssignmentExpression[ ConstructorCall/ClassType[ pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet') ]]/VariableAccess/@Name ) ) (: -- GuardedBy annotations are matched on SimpleName, so it matches all known GuardedBy annotations :) (: -- net.jcip.annotations.GuardedBy, javax.annotation.concurrent.GuardedBy, com.google.errorprone.annotations.concurrent.GuardedBy :) (: mutable types not annotated with GuardedBy :) and not(ModifierList/Annotation[@SimpleName='GuardedBy']) ] ]]> </value> </property> </properties> <example><![CDATA[ public class Bad { private static Map staticMapField = new HashMap(); // bad, class has synchronized method private Date date = new Date(); // bad, class has synchronized method public Date getDate() { return date; } public void setDate(Date d) { date = d; } public static synchronized Map getStaticMapField() { return staticMapField; } public static void setStaticMapField(Map smf) { staticMapField = smf; } } public class Good { @GuardedBy("this") private static Map staticMapField = new HashMap(); // good, guarded @GuardedBy("LOCK") private Date date = new Date(); // good, guarded public void setDate(Date d) { date = d; } public static synchronized Map getStaticMapField() { return staticMapField; } public static void setStaticMapField(Map smf) { staticMapField = smf; } } ]]></example> </rule> <rule name="AvoidUnguardedMutableFieldsInSharedObjects" message="Avoid unguarded non-final or mutable fields in objects shared among threads. Make final immutable or access thread-safely AND use @GuardedBy." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc07"> <description> Problem: Multiple threads typically access fields of a singleton or may access fields in session scoped objects. If a field or its reference is mutable, access is thread-unsafe and may cause corruption or visibility problems. It may also unintentionally mix-up session data. Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and jcip @GuardedBy or use volatile. Notes 1. Instances of Date, StringBuilder, URL and File are examples of mutable objects and should be avoided (or else guarded) as fields of shared objects. In case mutable fields are final and not modified after initialization (read-only) they are thread safe, however any modification to it is thread-unsafe. Since field modification is easily coded, avoid this situation. 2. Instances of classes like ArrayList, HashMap and HashSet are also mutable and should be properly wrapped with e.g. Collections.unmodifiableList after initialization (see TUTC03), or accessed thread-safely with e.g. Collections.synchronizedList or thread-safe implementations like ConcurrentHashMap. 3. For autowiring/injection, the assignment of the reference is thread safe, so final is not required. The unmodifiable/thread-safe requirement for that field still holds. Also make sure no other thread-unsafe assignment is made to that field. 4. In case you are sure the Component is used in single threaded context only (e.g. a Tasklet), annotate the class with @NotThreadSafe to make this explicit. 5. Use package private and @VisibleForTesting for methods used for JUnit only. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading,data-mix-up" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ (: -- GuardedBy annotations are matched on SimpleName, so it matches all known GuardedBy annotations :) (: -- net.jcip.annotations.GuardedBy, javax.annotation.concurrent.GuardedBy, com.google.errorprone.annotations.concurrent.GuardedBy :) (: known assumed singleton types by annotation :) //ClassDeclaration[ ( pmd-java:hasAnnotation('org.springframework.stereotype.Service') or pmd-java:hasAnnotation('org.springframework.stereotype.Component') or pmd-java:hasAnnotation('org.springframework.stereotype.Controller') or pmd-java:hasAnnotation('org.springframework.stereotype.Repository') or pmd-java:hasAnnotation('org.springframework.web.bind.annotation.RestController') or (pmd-java:hasAnnotation('javax.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('javax.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('javax.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) or (pmd-java:hasAnnotation('jakarta.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) ) (: not shared when request or prototype scope :) and not(pmd-java:hasAnnotation('org.springframework.context.annotation.Scope') and .//AnnotationMemberList/MemberValuePair[ StringLiteral[contains(@Image,'request') or contains(@Image,'prototype')] or FieldAccess[@Name='SCOPE_REQUEST' or @Name='SCOPE_PROTOTYPE']/TypeExpression[pmd-java:typeIs('org.springframework.beans.factory.config.ConfigurableBeanFactory')]] ) and not(pmd-java:hasAnnotation('org.springframework.context.annotation.RequestScope')) (: if @NotThreadSafe no checking :) and not(pmd-java:hasAnnotation('net.jcip.annotations.NotThreadSafe')) (: ConfigurationProperties assumed executed only once, no violation, except if Lombok Setter :) and not(pmd-java:hasAnnotation('org.springframework.boot.context.properties.ConfigurationProperties') and not(pmd-java:hasAnnotation('lombok.Setter')))] (: non-final and non-volatile fields :) //FieldDeclaration[ not(pmd-java:modifiers()=('final','volatile')) (: field not annotated with framework annotation or GuardedBy :) and not( pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.persistence.PersistenceContext') or pmd-java:hasAnnotation('jakarta.persistence.PersistenceContext')) or (pmd-java:hasAnnotation('javax.ejb.EJB') or pmd-java:hasAnnotation('jakarta.ejb.EJB')) or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or ModifierList/Annotation[@SimpleName='GuardedBy'] ) (: not in static inner class :) and not (ancestor::ClassDeclaration[@Nested=true() and @Static=true()]) (: field not on accessor method with assignment level annotated with framework annotation :) and not ( ./VariableDeclarator/VariableId/@Name = ancestor::ClassDeclaration//MethodDeclaration[ pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct')) or pmd-java:hasAnnotation('org.springframework.batch.core.annotation.BeforeStep') or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) ]//AssignmentExpression/*[1]/@Name ) or ( (: or non-final or volatile field of known mutable types :) ./ClassType[pmd-java:typeIs('java.util.Date') or pmd-java:typeIs('java.lang.StringBuilder') or pmd-java:typeIs('java.lang.StringBuffer') or pmd-java:typeIs('java.net.URL') or pmd-java:typeIs('java.io.File')] (: or mutable array :) or ( .//ArrayInitializer[@Length > 0] ) (: or in-line allocation of known mutable collection types :) or ( ./VariableDeclarator/ConstructorCall/ClassType[ pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet') ] ) (: or in-constructor allocation of known mutable collection types :) or ( ./VariableDeclarator/VariableId/@Name = ancestor::ClassDeclaration//ConstructorDeclaration//AssignmentExpression[ ConstructorCall/ClassType[ pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet') ]]/VariableAccess/@Name ) ) (: not annotated GuardedBy :) and not (ModifierList/Annotation[@SimpleName='GuardedBy']) ] ]]> </value> </property> </properties> <example><![CDATA[ @Component @Scope(value = SCOPE_APPLICATION, proxyMode = TARGET_CLASS) class AComponent2 { private String _url; // bad, unguarded accessor method public void setUrlBad(final String url) { _url = url; } } @Component class Bad { private Map<String, String> map1 = new HashMap<>(); // bad, reference and map is mutable private final Map<String, String> map2 = new HashMap<>(); // bad, map is mutable private final Map<String, String> map3 = new ConcurrentHashMap<>(); // good, concurrenthashmap is thread-safe @GuardedBy("this") private Map<String, String> map4 = new HashMap<>(); // good, guarded private Map<String, String> mapCtor1; // bad, reference and map is mutable private final Map<String, String> mapCtor2; // bad, constructed with known mutable type private final Map<String, String> mapCtor3; // good, concurrenthashmap is thread-safe @GuardedBy("this") private Map<String, String> mapCtor4; // good, guarded public Bad() { mapCtor1 = new HashMap<>(); mapCtor2 = new HashMap<>(); mapCtor3 = new ConcurrentHashMap<>(); mapCtor4 = new HashMap<>(); } } @Service class BadMutebleTypes { private final Date date = new Date(); // bad, Date is mutable private volatile StringBuilder strBuilder = new StringBuilder(); // bad, mutable type private final StringBuffer strBuffer = new StringBuffer(); // bad, mutable type private final String strOne = "abc"; // good, String is immutable private final String[] abc = new String[] {"a","b","c"}; // bad, elements can be replaced private final String[] nothing = new String[]{}; // good, no elements; not mutable } ]]></example> </rule> <rule name="AvoidUnguardedMutableInheritedFieldsInSharedObjects" message="Mutator method called on inherited field. Avoid unguarded mutable inherited fields in objects shared among threads. Make final immutable or access thread-safely AND use @GuardedBy." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc07"> <description> Problem: Multiple threads typically access fields of a singleton or may access fields in session scoped objects. If a (inherited) field or its reference is mutable, access is thread-unsafe and may cause corruption or visibility problems. It may also unintentionally mix-up session data. Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and jcip @GuardedBy or use volatile. Notes 1. Instances of Date, StringBuilder, URL and File are examples of mutable objects and should be avoided (or else guarded) as fields of shared objects. In case mutable fields are final and not modified after initialization (read-only) they are thread safe, however any modification to it is thread-unsafe. Since field modification is easily coded, avoid this situation. 2. Instances of classes like ArrayList, HashMap and HashSet are also mutable and should be properly wrapped with e.g. Collections.unmodifiableList after initialization (see TUTC03), or accessed thread-safely with e.g. Collections.synchronizedList or thread-safe implementations like ConcurrentHashMap. 3. For autowiring/injection, the assignment of the reference is thread safe, so final is not required. The unmodifiable/thread-safe requirement for that field still holds. Also make sure no other thread-unsafe assignment is made to that field. 4. In case you are sure the Component is used in single threaded context only (e.g. a Tasklet), annotate the class with @NotThreadSafe to make this explicit. 5. Use package private and @VisibleForTesting for methods used for JUnit only. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="data-mix-up,jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ (: known assumed singleton types by annotation :) //ClassDeclaration[ ( pmd-java:hasAnnotation('org.springframework.stereotype.Service') or pmd-java:hasAnnotation('org.springframework.stereotype.Component') or pmd-java:hasAnnotation('org.springframework.stereotype.Controller') or pmd-java:hasAnnotation('org.springframework.stereotype.Repository') or pmd-java:hasAnnotation('org.springframework.web.bind.annotation.RestController') or (pmd-java:hasAnnotation('javax.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('javax.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('javax.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) or (pmd-java:hasAnnotation('jakarta.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) ) (: not shared when request or prototype scope :) and not(pmd-java:hasAnnotation('org.springframework.context.annotation.Scope') and .//AnnotationMemberList/MemberValuePair[ StringLiteral[contains(@Image,'request') or contains(@Image,'prototype')] or FieldAccess[@Name='SCOPE_REQUEST' or @Name='SCOPE_PROTOTYPE']/TypeExpression[pmd-java:typeIs('org.springframework.beans.factory.config.ConfigurableBeanFactory')]] ) and not(pmd-java:hasAnnotation('org.springframework.context.annotation.RequestScope')) (: if @NotThreadSafe no checking :) and not(pmd-java:hasAnnotation('net.jcip.annotations.NotThreadSafe')) (: ConfigurationProperties assumed executed only once, no violation, except if Lombok Setter :) and not(pmd-java:hasAnnotation('org.springframework.boot.context.properties.ConfigurationProperties') and not(pmd-java:hasAnnotation('lombok.Setter')))] (: any method call:) //MethodCall[ (: class extends a base class :) ancestor::ClassDeclaration//ExtendsList (: not in static inner class :) and not (ancestor::ClassDeclaration[@Nested=true() and @Static=true()]) (: has arguments (a method call), and a compound like date.setTime, map.put, list.set :) and matches(@MethodName,"^(set|put|clear|add|remove|replace)") (: not a field of this class, no param or local :) and ./VariableAccess[ not (@Name = ancestor::ClassDeclaration//FieldDeclaration/VariableDeclarator/VariableId/@Name or @Name = (ancestor::MethodDeclaration|ancestor::ConstructorDeclaration)//LocalVariableDeclaration/VariableDeclarator/VariableId/@Name or @Name = (ancestor::MethodDeclaration|ancestor::ConstructorDeclaration)//FormalParameter/VariableId/@Name or @Name = ancestor::CatchClause/CatchParameter/VariableId/@Name or @Name = ancestor::LambdaExpression//LambdaParameter/VariableId/@Name )] (: not synchronized :) and not (ancestor::SynchronizedStatement or ancestor::MethodDeclaration[pmd-java:modifiers()='synchronized']) (: field not on accessor method with assignment, annotated with framework annotation :) and not( ancestor::MethodDeclaration[ pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct')) or pmd-java:hasAnnotation('org.springframework.batch.core.annotation.BeforeStep') or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) ] (: field not assigned in non-public accessor method annotated with VisibleForTesting :) or (ancestor::MethodDeclaration[pmd-java:hasAnnotation('com.google.common.annotations.VisibleForTesting') and not(@Visibility='public')]) (: annotation is not enough? :) or ancestor::MethodDeclaration[@Name='afterPropertiesSet'] ) ] ]]> </value> </property> </properties> <example><![CDATA[ @Component public class MutateInheritedFieldTryout extends SynchronizedTryout { final Map mapField = getMap(); // found by other rule private String removed; // found by other rule public String getDateString() { return date.toString(); } public void mutateDateBad() { date.setTime(100); // bad } public void mutateMap(String key, String val) { Collections.sort(null); map.clear(); // bad - 25 if (map.put(key, val)) { // bad removed = ""; } removed = map.remove("invalidKey"); // bad } public Object readMap(String key) { return map.get(key); } public synchronized void mutateSyncMap(String key, String val) { map.put(key, val); } public void mutateMapField(String key, String val) { mapField.put(key, val); // good, fields checked in other rule } public void mutateMapParam(Map map, String key, String val) { map.put(key, val); // good, param map shadows inherited field } public void mutateMapLocal(String key, String val) { Map map = new HashMap(); map.put(key, val); // good, local map shadows inherited field } } ]]></example> </rule> <rule name="AvoidUserDataInSharedObjects" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Do *not* use user data in shared objects, it causes user data mix-up." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#ssc03"> <description>The name of the field indicates user data. Problem: the field will be shared among users. If it is different data for each user, it can mix-up: a user may access data of another user, this is really bad. Solution: Do *not* put the user related data in a shared object e.g. by Spring @Component annotation. Use a POJO. Or, if it is not user data, rename the field. (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="correctness,data-mix-up,jpinpoint-rule,multi-threading,suspicious" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: annotations that make singleton like components in Spring and EJB :) //ClassDeclaration[ ( (pmd-java:hasAnnotation('org.springframework.stereotype.Service') or pmd-java:hasAnnotation('org.springframework.stereotype.Component') or pmd-java:hasAnnotation('org.springframework.stereotype.Controller') or pmd-java:hasAnnotation('org.springframework.stereotype.Repository') or pmd-java:hasAnnotation('org.springframework.web.bind.annotation.RestController') or (pmd-java:hasAnnotation('javax.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('javax.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('javax.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) or (pmd-java:hasAnnotation('jakarta.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) or (pmd-java:hasAnnotation('org.springframework.boot.context.properties.ConfigurationProperties') and pmd-java:hasAnnotation('lombok.Setter')) ) (: no violation if @ConfigurationProperties *and not* @Setter :) and not(pmd-java:hasAnnotation('org.springframework.boot.context.properties.ConfigurationProperties') and not(pmd-java:hasAnnotation('lombok.Setter'))) ) (: no @RequestScope annotation :) and not( pmd-java:hasAnnotation('org.springframework.context.annotation.Scope') and .//AnnotationMemberList/MemberValuePair[ StringLiteral[contains(@Image,'request') or contains(@Image,'prototype')] or FieldAccess[@Name='SCOPE_REQUEST' or @Name='SCOPE_PROTOTYPE']/TypeExpression[pmd-java:typeIs('org.springframework.beans.factory.config.ConfigurableBeanFactory')] ] ) (: no request or prototype @Scope, literal or via constant :) and not(pmd-java:hasAnnotation('org.springframework.web.context.annotation.RequestScope')) ] (: no final or static fields, and not fields in static inner classes and no primitives :) //FieldDeclaration[not( pmd-java:modifiers()=('static','final') or ancestor::ClassDeclaration[pmd-java:modifiers()='static'] or ./PrimitiveType ) ]/VariableDeclarator/VariableId[ matches(@Name, 'User[Id|Ref|Reference]*$|Customer[Id|Ref|Reference]*$|Session[Id|Ref|Reference]*$|Order[Id|Ref|Reference|List]*$|Account[Id|Ref|Reference|List]*$|Transaction[Id|Ref|Reference|List]*$|Contract[Id|Ref|Reference|List]*$' , 'i') ] ]]></value> </property> </properties> <example> <![CDATA[ @Component @Data public class VMRData { private String channel; private String vmrId; private List<OrderDetails> orderList; // bad private Headers headers; private List<OrderReference> ordersRequiringAdditionalSignature; private String authUser; // bad private String executionDate; private String minimumLevel; private String sessionId; // bad private String payloadData; private String vmrUserId; // bad private String userref; // bad private String customerReference; // bad private String contract; // bad } @Component @Setter @Getter class VMROrderDetails { private static final String DESC_OF_ORDER = "order details"; // ok private final VMROrderDetails finalOrder = new VMROrderDetails(); // ok private OrderReference orderReference; // bad private Account originatorAccount; // bad private Amount amount; private String action; private List<CancellationHistory> cancellationHistories; private String modifyType; private String order; // bad private String transactionRef; // bad } ]]> </example> </rule> <rule name="AvoidVolatileInPrototypeScope" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="No need for volatile in prototype scope" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc10"> <description>A field is defined as volatile while the class has prototype scope. Problem: volatile has some overhead, especially for writes. When getting the bean from the Spring applicationContext, prototype scope means that each invocation creates a new object so the field is not shared. Solution: Since only one thread can access the field, there is no need for volatile and it can be removed. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="confusing,jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //FieldDeclaration[ pmd-java:modifiers()='volatile' and ancestor::ClassDeclaration[ pmd-java:hasAnnotation('org.springframework.context.annotation.Scope')and .//AnnotationMemberList/MemberValuePair[ StringLiteral[contains(@Image,'prototype')] or FieldAccess[@Name='SCOPE_PROTOTYPE']/TypeExpression[pmd-java:typeIs('org.springframework.beans.factory.config.ConfigurableBeanFactory')]] ] ] ]]> </value> </property> </properties> <example> <![CDATA[ @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Component class Bad { private volatile String id; } @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Component class Good { private String id; } ]]> </example> </rule> <rule name="NotProperlySynchronizingOnFieldWhileUsingGuardedBy" message="Not properly synchronizing access of field while using @GuardedBy(field)" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://jcip.net/annotations/doc/index.html"> <description> Problem: The field to which the GuardedBy annotation is applied is not accessed thread-safely as described by GuardedBy. Solution: Make access thread-safe: synchronize access by a synchronized(LOCK) block with LOCK being a final field. Note that methods with annotations @Autowired, @PostConstruct, @BeforeStep, @Value and @Inject are ignored. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ (: -- GuardedBy annotations are matched on SimpleName, so it matches all known GuardedBy annotations :) (: -- net.jcip.annotations.GuardedBy, javax.annotation.concurrent.GuardedBy, com.google.errorprone.annotations.concurrent.GuardedBy :) //MethodDeclaration[ (: method annotated with known frameworks :) not(pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct')) or pmd-java:hasAnnotation('org.springframework.batch.core.annotation.BeforeStep') or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) ) ] (: a variable which should (potentially) be guarded and not var of synchronized statement itself :) //(AssignmentExpression/*[1]|VariableAccess[not(parent::SynchronizedStatement)])[ (: used in a class with guarded-by literal *IS* a field (should be private final) :) @Name = ancestor::ClassDeclaration//FieldDeclaration[ModifierList/Annotation[@SimpleName='GuardedBy']//MemberValuePair/StringLiteral[ @ConstValue = ancestor::ClassDeclaration//FieldDeclaration/VariableDeclarator/VariableId/@Name] ]/VariableDeclarator/VariableId/@Name (: and *IS NOT* in a synchronized block with the GuardedBy literal:) and not(ancestor::SynchronizedStatement/VariableAccess/@Name = ancestor::ClassDeclaration//FieldDeclaration[ModifierList/Annotation[@SimpleName='GuardedBy']]//MemberValuePair/StringLiteral/@ConstValue) ] , //MethodDeclaration[ (: method annotated with known frameworks :) not(pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct')) or pmd-java:hasAnnotation('org.springframework.batch.core.annotation.BeforeStep') or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) ) ] (: a variable which should (potentially) be guarded and not var of synchronized statement itself :) //(AssignmentExpression/*[1]|VariableAccess[not(parent::SynchronizedStatement)])[ (: used in a class with guarded-by literal *IS NOT* a field (should be private final) :) @Name = ancestor::ClassDeclaration//FieldDeclaration[ ModifierList/Annotation[@SimpleName='GuardedBy']//MemberValuePair/StringLiteral[ not(@ConstValue = 'this' or @ConstValue = ancestor::ClassDeclaration//FieldDeclaration/VariableDeclarator/VariableId/@Name)] ]/VariableDeclarator/VariableId/@Name ] ]]></value> </property> </properties> <example> <![CDATA[ class Bad1 { private final Object LOCK = new Object(); @GuardedBy("LOCK") private String txt; public String getText() { return txt; } // bad public synchronized void setText(String t) { txt = t; } // bad } class Good1 { private final Object LOCK = new Object(); @GuardedBy("LOCK") private String txt; public String getText() { synchronized(LOCK) { return txt; } } public void setText(String t) { synchronized(LOCK) { txt = t; } } } ]]> </example> </rule> <rule name="NotProperlySynchronizingOnThisWhileUsingGuardedBy" message="Not properly synchronizing access of field while using @GuardedBy('this')" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://jcip.net/annotations/doc/index.html"> <description> Problem: The field to which this annotation is applied should only be accessed when holding the built-in 'this' lock by using synchronized. Solution: Make access thread-safe: synchronize access by method modifier or a synchronized(this) block. Note that methods with annotations @Autowired, @PostConstruct, @BeforeStep, @Value and @Inject are ignored. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[//MethodDeclaration[ (: method not synchronized or static :) (not(pmd-java:modifiers() = 'synchronized') or pmd-java:modifiers() = 'static') and not(pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct')) or pmd-java:hasAnnotation('org.springframework.batch.core.annotation.BeforeStep') or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) ) ]//(AssignmentExpression/*[1]|VariableAccess)[ (: not is synchronized block (with this) or method :) not(ancestor::SynchronizedStatement[./ThisExpression] or ancestor::MethodDeclaration[pmd-java:modifiers()='synchronized']) (: -- GuardedBy annotations are matched on SimpleName, so it matches all known GuardedBy annotations :) (: -- net.jcip.annotations.GuardedBy, javax.annotation.concurrent.GuardedBy, com.google.errorprone.annotations.concurrent.GuardedBy :) (: field GuardedBy("this") :) and @Name = (ancestor::ClassDeclaration|ancestor::EnumDeclaration)//FieldDeclaration[ ./ModifierList/Annotation[@SimpleName='GuardedBy' ]//MemberValuePair/StringLiteral[@Image='"this"']]/VariableDeclarator/VariableId/@Name ] ]]></value> </property> </properties> <example> <![CDATA[ class Bad1 { @GuardedBy("this") private String txt; public String getText() { return txt; } // bad public void setText(String t) { txt = t; } // bad } class Good1 { @GuardedBy("this") private String txt; public synchronized String getText() { return txt; } public synchronized void setText(String t) { txt = t; } } ]]> </example> </rule> <rule name="SynchronizingForLocalVars" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Local variables don't need synchronization" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc12"> <description>In the synchronized block, only local variables seem to be accessed. Problem: synchronization has overhead and may introduce lock contention. Solution: Remove synchronized because local variables are only accessible by the owning thread and are not shared. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="confusing,cpu,jpinpoint-rule,multi-threading,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //SynchronizedStatement[ count(./Block//(VariableAccess|FieldAccess)[@Name = ancestor::ClassDeclaration//FieldDeclaration//VariableId/@Name]) = 0 ] ]]></value> </property> </properties> <example> <![CDATA[ public class Foo { private Map<String, String> mapField; protected Map<String, String> bad() { Map<String, String> addHeaders = MDC.getCopyOfContextMap(); synchronized (this) { // bad if (addHeaders == null) { addHeaders = new HashMap<>(); } } return addHeaders; } protected Map<String, String> good() { Map<String, String> addHeaders = MDC.getCopyOfContextMap(); synchronized (this) { if (mapField == null) { mapField = new HashMap<>(); addHeaders = new HashMap<>(); } } return addHeaders; } } ]]> </example> </rule> <!-- END Included file 'concurrent.xml' --> <!-- BEGIN Included file 'enterprise.xml' --> <rule name="DefineConcurrencyForJavaEESingleton" message="For JavaEE @Singleton, make @ConcurrencyManagement explicit and define @Lock on class level or for all public methods." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc13"> <description> Problem: A JavaEE @Singleton has default @ConcurrencyManagement CONTAINER and write locks. Using defaults is unclear and write locks typically cause much more contention than read locks. Solution: Make @ConcurrencyManagement and @Lock-s explicit, @Lock on class level or on all public methods. Or use BEAN managed for full control using e.g. synchronized or volatile. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="confusing,cpu,jpinpoint-rule,multi-threading,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //ClassDeclaration[pmd-java:hasAnnotation('javax.ejb.Singleton')] (: @Singleton without @ConcurrencyManagement :) [not(pmd-java:hasAnnotation('javax.ejb.ConcurrencyManagement')) (: or @Singleton with @ConcurrencyManagement CONTAINER and not a class level @Lock or has public methods without @Lock :) or pmd-java:hasAnnotation('javax.ejb.ConcurrencyManagement') and .//Annotation[@SimpleName='ConcurrencyManagement']//(FieldAccess|VariableAccess)[@Name='CONTAINER'] and (not(pmd-java:hasAnnotation('javax.ejb.Lock'))) and (count(.//MethodDeclaration[@Visibility='public' and not(exists(.//Annotation[@SimpleName='Lock']))]) > 0) ] , //ClassDeclaration[pmd-java:hasAnnotation('jakarta.ejb.Singleton')] (: @Singleton without @ConcurrencyManagement :) [not(pmd-java:hasAnnotation('jakarta.ejb.ConcurrencyManagement')) (: or @Singleton with @ConcurrencyManagement CONTAINER and not a class level @Lock or has public methods without @Lock :) or pmd-java:hasAnnotation('jakarta.ejb.ConcurrencyManagement') and .//Annotation[@SimpleName='ConcurrencyManagement']//(FieldAccess|VariableAccess)[@Name='CONTAINER'] and (not(pmd-java:hasAnnotation('jakarta.ejb.Lock'))) and (count(.//MethodDeclaration[@Visibility='public' and not(exists(.//Annotation[@SimpleName='Lock']))]) > 0) ] ]]> </value> </property> </properties> <example> <![CDATA[ @Singleton // bad - 1. @ConcurrencyManagement missing 2. @Lock missing on public method class SingletonBad { private String state; public String getState() { return state; } @Lock(LockType.WRITE) public void setState(String newState) { state = newState; } } @Singleton @ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) class SingletonGood { private String state; @Lock(LockType.READ) public String getState() { return state; } @Lock(LockType.WRITE) public void setState(String newState) { state = newState; } } ]]> </example> </rule> <!-- END Included file 'enterprise.xml' --> <!-- BEGIN Included file 'remoting.xml' --> <rule name="AvoidClientHttpRequestFactorySupplier" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Avoid use of ClientHttpRequestFactorySupplier." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi17"> <description>Problem: The org.springframework.boot.web.client.ClientHttpRequestFactorySupplier may return a HttpComponentsClientHttpRequestFactory which you supply as @Bean, however, this can silently go wrong and e.g. an unconfigured SimpleClientHttpRequestFactory can be returned. Default pool size and timeouts will be used, possibly resulting in very slow connection use. Solution: Provide your own supplier with explicit pool sizing and timeouts by a class implementing Supplier. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="confusing,jpinpoint-rule,performance,suspicious" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ConstructorCall[pmd-java:typeIs("org.springframework.boot.web.client.ClientHttpRequestFactorySupplier")] ]]> </value> </property> </properties> <example> <![CDATA[ import org.springframework.boot.web.client.ClientHttpRequestFactorySupplier; import org.springframework.web.client.RestTemplate; class Bad { void bad() { RestTemplate restTemplate = new RestTemplateBuilder(rt -> rt.getInterceptors() .add((request, body, execution) -> { request.getHeaders().add("SomeKey", someKey); return execution.execute(request, body); })) .requestFactory(new ClientHttpRequestFactorySupplier()) // bad .uriTemplateHandler(defaultUriBuilderFactory) .build(); return restTemplate; } } class MyClientHttpRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> { public ClientHttpRequestFactory get() { PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(); poolingHttpClientConnectionManager.setDefaultMaxPerRoute(MAX_CONN_PER_ROUTE); poolingHttpClientConnectionManager.setMaxTotal(MAX_CONN_TOTAL); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(poolingHttpClientConnectionManager) .disableConnectionState() .build(); return new HttpComponentsClientHttpRequestFactory(httpClient); } } and use it to replace the bad line in Bad example: .requestFactory(new MyClientHttpRequestFactorySupplier()) // good ]]> </example> </rule> <rule name="AvoidDeprecatedHttpConnectors" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Avoid the use of deprecated/thread-unsafe HTTP connectors" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi06"> <description>Problem: Several HTTP client connection managers are thread-unsafe which may cause session data mix-up or have other issues for which they were made deprecated. Solutions: Upgrade to httpclient-4.5+ and use org.apache.http.impl.conn.PoolingHttpClientConnectionManager and e.g. org.apache.http.impl.client.HttpClientBuilder. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="data-mix-up,deprecated,jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ImportDeclaration[starts-with(@ImportedName, 'org.apache.commons.httpclient') or @ImportedName='org.apache.http.conn.ClientConnectionManager' or @ImportedName='org.apache.http.impl.conn.PoolingClientConnectionManager' or @ImportedName='org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager' or @ImportedName='org.apache.http.impl.conn.SingleClientConnManager' or @ImportedName='org.apache.http.impl.client.DefaultHttpClient' or @ImportedName='org.apache.http.impl.client.SystemDefaultHttpClient' or @ImportedName='org.apache.http.conn.ClientConnectionManager' ] | //ClassType[pmd-java:typeIs('org.apache.commons.httpclient.SimpleHttpConnectionManager') or pmd-java:typeIs('org.apache.http.conn.ClientConnectionManager') or pmd-java:typeIs('org.apache.http.impl.conn.PoolingClientConnectionManager') or pmd-java:typeIs('org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager') or pmd-java:typeIs('org.apache.http.impl.conn.SingleClientConnManager') or pmd-java:typeIs('org.apache.http.impl.client.DefaultHttpClient') or pmd-java:typeIs('org.apache.http.impl.client.SystemDefaultHttpClient') or pmd-java:typeIs('org.apache.http.conn.ClientConnectionManager') or pmd-java:typeIs('org.apache.commons.httpclient.MultiThreadedHttpConnectionManager') or pmd-java:typeIs('org.apache.commons.httpclient.HttpClient') ] ]]></value> </property> </properties> <example> <![CDATA[ import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; // bad import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; public class Bad { private MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager(); // bad } class Good { private PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); } ]]> </example> </rule> <rule name="AvoidDeprecatedHystrix" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Netflix Hystrix is deprecated. Use an alternative like resilience4j" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi11"> <description>Problem: Hystrix is not actively maintained anymore. Solution: Netflix recommends to use open source alternatives like resilience4j. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="deprecated,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ImportDeclaration[starts-with(@ImportedName, "com.netflix.hystrix")] ]]> </value> </property> </properties> </rule> <rule name="AvoidDiscardingHttpClientConfig" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Do not replace the configured httpClient provided in the constructor of the factory." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi15"> <description>Problem: Apache HttpComponentsClientHttpRequestFactory has a constructor which takes a HttpClient and also a setter: setHttpClient. If you use both on the same factory, you discard all configuration done on the one provided in the constructor because it is replaced by the one provided to the setter. Solution: Don't use both on the same factory, provide the HttpClient only once to the factory. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="confusing,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[pmd-java:matchesSig('org.springframework.http.client.HttpComponentsClientHttpRequestFactory#setHttpClient(_)')][VariableAccess/@Name = ancestor::MethodDeclaration//VariableDeclarator[ConstructorCall[pmd-java:typeIs('org.springframework.http.client.HttpComponentsClientHttpRequestFactory')][ArgumentList[@Size > 0]]] /VariableId/@Name] ]]> </value> </property> </properties> <example> <![CDATA[ class Bad { ClientHttpRequestFactory getFactory(HttpClientConfiguration config) { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create() .setMaxConnTotal(config.getMaxTotalConnections()) .setMaxConnPerRoute(config.getMaxConnPerRoute()) .build()); factory.setHttpClient(createHttpClient(config)); //bad return factory; } } class Good { ClientHttpRequestFactory getFactory(HttpClientConfiguration config) { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(createFullyConfiguredHttpClient(config)); return factory; } } ]]> </example> </rule> <rule name="AvoidExcessiveClassloadingWithSaajSoap" message="Avoid excessive class loading with SAAJ SOAP messaging, use alternative Axiom, or system properties." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi23"> <description>Problem: SAAJ uses DOM to load the XML document in memory which uses a TransformerFactory. The implementation class of it is loaded on every call which causes lock contention under load. This means long response times. Solution: If possible, use Axiom SOAP messaging which uses the faster StAX. If you have/want to stick to SAAJ, set the proper system properties to prevent the excessive class loading. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassType[pmd-java:typeIs('org.springframework.ws.soap.saaj.SaajSoapMessageFactory') and not (ancestor::ClassBody/Initializer//MethodCall[pmd-java:matchesSig('java.lang.System#setProperty(_,_)') and ArgumentList/*[1][@ConstValue='javax.xml.transform.TransformerFactory']])] ]]></value> </property> </properties> <example> <![CDATA[ import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; class Foo { private final SaajSoapMessageFactory mfField; // bad, also possibly thread-unsafe public WebServiceTemplate getTemplate(final String uri, final HttpClientMessageSender httpClientMessageSender) throws SOAPException { SoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance()); // bad //.. } } //To solve, add to the above the proper implementing classes: static { System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); System.setProperty("javax.xml.soap.MessageFactory", "com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl"); } ]]> </example> </rule> <rule name="AvoidHardcodedConnectionConfig" message="Avoid hardcoded connection configuration." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi22"> <description>Problem: configuring connection settings like timeouts and pool sizes in code (int values) makes it difficult to manage and tune these settings. Solution: use property files, e.g. yml, to define the values for these settings for each called service. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="bad-practice,jpinpoint-rule,performance" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //FieldDeclaration[PrimitiveType/@Kind='int']/VariableDeclarator [VariableId[@Final = true()] [matches(upper-case(@Name), 'TIMEOUT|DURATIONOUT') or (contains(upper-case(@Name), 'MAX') and contains(upper-case(@Name), 'ROUTE'))] ] [NumericLiteral or (VariableId/@Name=ancestor::ClassBody//ConstructorDeclaration//AssignmentExpression[NumericLiteral]/*[1]/@Name and not(VariableId/@Name=ancestor::ClassBody//ConstructorDeclaration//FormalParameter[pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value')]/VariableId/@Name)) ] ]]></value> </property> </properties> <example> <![CDATA[ class AvoidHardcodedConnectionConfig { private static final int CONNECTION_TIMEOUT = 50000;// bad private static final int SOCKET_TIMEOUT = 250000;// bad private static final int TRANSACTION_TIMEOUT_SECONDS = 2;// bad private static final int CONNECTION_TIMEOUTMILLIS = 1000;// bad private static final int MAX_CONN_PER_ROUTE = 10;// bad static final int MAX_PER_ROUTE = 100;// bad final int MAX_NUM_ROUTES = 2; // bad int some_timeout_ms = 300; //good, variable private static final String LOGIN_TIMEOUT_PROPERTY_KEY = "loginTimeout"; // good, not an int final int other_timeout_s_blank; // bad, blank final and hard coded private final int connectTimeoutMillis; // good, not hard coded AvoidHardcodedConnectionConfig(@Value("${connect_timeout:1000}") int connectTimeoutMillis) { this.connectTimeoutMillis = connectTimeoutMillis; // good, not hard coded other_timeout_s_blank = 5; // field bad final int timeout_s_local = 4; // good, method level int otherTimeoutInS = 3; // good, var } } ]]> </example> </rule> <rule name="AvoidHttpHostOneArgumentConstructor" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Avoid the HttpHost constructor with only hostname, provide port number and protocol as well." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi16"> <description>Problem: the HttpHost constructor with one argument must only be provided with a host name, the default port 80 and protocol http are implied. The mistake of providing a URL and assuming it will be parsed into hostname, port and protocol is easily made. When this HttpHost is then used for a route and stored socketConfig for, port 80 is added for the host and the socketConfig is stored with the wrong key and will not be used. It is typically difficult to find out if the config is actually used. Note that 1. https://github.com/jborgers/http-client-monitor or 2. micrometer and apache http client 4 and 5 metrics with spring boot actuator, help here. Solution: Use the HttpHost constructor with 2 (including port) or preferably 3 arguments (including port and protocol). (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="confusing,jpinpoint-rule,suspicious" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ConstructorCall[pmd-java:typeIs("org.apache.http.HttpHost")][ArgumentList[@Size=1]] ]]> </value> </property> </properties> <example> <![CDATA[ import org.apache.http.HttpHost; class Foo { private static final String URL = "localhost:8080"; private static final HttpHost hostBad1 = new HttpHost("localhost:8080"); // bad void bar() { HttpHost hostBad2 = new HttpHost(URL);//bad HttpHost hostGood1 = new HttpHost("localhost", 8080, "http"); //good } } ]]> </example> </rule> <rule name="AvoidJAXBUtil" language="java" message="The JAXB utility class is not optimized for performance." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuoxar04"> <description>Problem: JAXB utility methods do not reuse JAXBContext when more that one context is used. Solution: use JAXB API directly for marshalling and unmarshalling to gain all the performance benefits as described in IUOXAR04 and IUOXAR06. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassType[pmd-java:typeIs('javax.xml.bind.JAXB') or pmd-java:typeIs('jakarta.xml.bind.JAXB')] ]]></value> </property> </properties> <example> <![CDATA[ public class XMLConversion { public <T> T myUnmarshal(final Source response, final Class<T> clazz) { return JAXB.unmarshal(response, clazz); // bad } public void myMarshal(final Object response, StringWriter stringWriter) { JAXB.marshal(response, stringWriter); // bad } } ]]> </example> </rule> <rule name="AvoidLeakingRetryEventConsumers" message="Avoid leaking of retry event consumers." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#pml06"> <description>Problem: A resilience4j retry event consumer is added to a retry event publisher for every method call. Likely a lambda retaining one or more objects. This will result in a growing list of consumers: a memory leak. Besides, the event will be sent to the growing number of consumers, taking more and more CPU time. Solution: Only call EventPublisher.onRetry (that is, add a consumer) in the same scope as the Retry instance lives. Note there is no way to unregister a consumer. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,memory,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: any on-method: onRetry, onError, .. :) //MethodDeclaration//MethodCall[starts-with(@MethodName, 'on')]/MethodCall[pmd-java:matchesSig('io.github.resilience4j.retry.Retry#getEventPublisher()')] (: it is a field :) [VariableAccess/@Name = ancestor::ClassBody/FieldDeclaration//VariableId/@Name] ]]></value> </property> </properties> <example> <![CDATA[ import io.github.resilience4j.retry.Retry; public class Foo { Retry retryField; RetryRegistry reg; final AtomicInteger retryCountField = new AtomicInteger(); Foo() { retryField = reg.retry("one per service"); retryField.getEventPublisher().onRetry(event -> retryCountField.getAndIncrement()); // good } void callService() { AtomicInteger retryCountLocal = new AtomicInteger(); retryField.getEventPublisher().onRetry(event -> retryCountLocal.getAndIncrement()); // bad, lambda and AtomicInt leak Retry retryLocal = reg.retry("one per method call"); retryLocal.getEventPublisher().onRetry(event -> retryCountLocal.getAndIncrement()); // good, no leak // same for onSuccess, onError, onIgnoredError } } ]]> </example> </rule> <rule name="AvoidModifyingObjectMapper" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="ObjectMapper is configured/modified, only initialize it right after construction." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuojar02"> <description>Problem: ObjectMapper is thread-safe only after configuration. Configuring an ObjectMapper is not thread-safe. Solution: Avoid configuring objectMappers except when initializing: right after construction. It is recommended to create ObjectReaders and ObjectWriters from ObjectMapper and pass those around since they are immutable and therefore thread-safe. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: exclude method annotated with PostConstruct :) //MethodDeclaration [not (pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct'))] (: exclude factory where config is in method returning 'this' and one method, typically build(), returning objectMapper :) [not(.//ReturnStatement/ThisExpression) or ancestor::ClassBody[count(.//ReturnStatement/VariableAccess[pmd-java:typeIs('com.fasterxml.jackson.databind.ObjectMapper')]) != 1]] (: modify operations on objectMapper in a method where objectMapper is a formal parameter or field :) //MethodCall [matches(@MethodName, '^config.*|^set.*|^register.*|^find.*|^disable.*|^enable.*|^activate.*|^deactivate.*|^add.*|^clear.*')] [VariableAccess[@Name=ancestor::MethodDeclaration//FormalParameter[pmd-java:typeIs('com.fasterxml.jackson.databind.ObjectMapper')]/VariableId/@Name or @Name=ancestor::ClassBody/FieldDeclaration[ClassType[pmd-java:typeIs('com.fasterxml.jackson.databind.ObjectMapper')]]/VariableDeclarator/VariableId/@Name] ] , (: modify operations on objectMapper in a constructor where objectMapper is a formal parameter or static field :) //ConstructorDeclaration//MethodCall [matches(@MethodName, '^config.*|^set.*|^register.*|^find.*|^disable.*|^enable.*|^activate.*|^deactivate.*|^add.*|^clear.*')] [VariableAccess/@Name= (ancestor::ConstructorDeclaration//FormalParameter|ancestor::ClassBody//FieldDeclaration[pmd-java:modifiers()=('static')]/ClassType)[pmd-java:typeIs('com.fasterxml.jackson.databind.ObjectMapper')]/..//VariableId/@Name] ]]> </value> </property> </properties> <example> <![CDATA[ public class OldStyle { private static final ObjectMapper staticObjectMapper = new ObjectMapper(); private final ObjectMapper mapperField = new ObjectMapper(); static { staticObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // good } public OldStyle() { mapperField.setSerializationInclusion(JsonInclude.Include.NON_NULL); // good } ObjectMapper bad(ObjectMapper mapper) { mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // bad return mapper; } } public class NewStyle { private static final ObjectWriter staticObjectWriter = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL).writer(); // good } ]]> </example> </rule> <rule name="AvoidObjectMapperAsField" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Avoid ObjectMapper as field, use ObjectReader or ObjectWriter instead." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuojar02"> <description>Problem: Configuring an ObjectMapper is thread-unsafe. Solution: Create ObjectReaders and ObjectWriters from ObjectMapper and only share those as field, since they are immutable and therefore thread-safe. Exceptions: A convertValue method is not provided by ObjectReader nor ObjectWriter, therefore in those cases this rule is not applied. Also, when used like jaxMsgConverter.setObjectMapper(objectMapper) it is not considered a violation. And, when the class implements ContextResolver<ObjectMapper>. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //FieldDeclaration[ClassType/pmd-java:typeIs('com.fasterxml.jackson.databind.ObjectMapper')]//VariableId (: exception: do not match if that field name is used in its class with a convertValue method :) [not(@Name = ancestor::ClassBody//MethodCall[pmd-java:matchesSig('com.fasterxml.jackson.databind.ObjectMapper#convertValue(_,_)')]/VariableAccess/@Name)] (: exception: do not match if used with jaxMsgConverter.setObjectMapper(objectMapper) :) [not(@Name = ancestor::ClassBody//MethodCall[pmd-java:matchesSig('org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#setObjectMapper(_)')]/ArgumentList/VariableAccess/@Name)] (: exception: do not match if class implements ContextResolver<ObjectMapper> :) [not(ancestor::ClassDeclaration/ImplementsList/ClassType[pmd-java:typeIs('javax.ws.rs.ext.ContextResolver')]/TypeArguments/ClassType[pmd-java:typeIs('com.fasterxml.jackson.databind.ObjectMapper')])] ]]> </value> </property> </properties> <example> <![CDATA[ public class BadStyle { private static final ObjectMapper staticObjectMapper = new ObjectMapper(); // bad private final ObjectMapper mapperField = new ObjectMapper(); //bad } class GoodStyle { private static final ObjectWriter staticObjectWriter = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL).writer(); // good } ]]> </example> </rule> <rule name="AvoidReactorDebugOverhead" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Do not use Reactor debugging in production, it has much overhead." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi14"> <description>Problem: For troubleshooting Reactor, Blockhound can be used. It needs proper stack traces which can be achieved by Hooks.onOperatorDebug(). This can have much CPU overhead. Solution: Remove Hooks.onOperatorDebug() when not debugging. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[pmd-java:matchesSig('reactor.core.publisher.Hooks#onOperatorDebug()')] ]]> </value> </property> </properties> <example> <![CDATA[ import reactor.core.publisher.Hooks; public class Foo { public void bar() { Hooks.onOperatorDebug(); //bad } } ]]> </example> </rule> <rule name="AvoidRecreatingHttpClient" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="An HttpClient is created and combined with request-response." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi12"> <description>Problem: Apache HttpClient with its connection pool and timeouts should be setup once and then used for many requests. It is quite expensive to create and can only provide the benefits of pooling when reused in all requests for that connection. Solution: Create/build HttpClient with proper connection pooling and timeouts once, and then use it for requests. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassDeclaration[not(pmd-java:hasAnnotation('org.springframework.context.annotation.Configuration'))] //MethodDeclaration//MethodCall[ pmd-java:matchesSig('org.apache.http.impl.client.HttpClientBuilder#create()') or pmd-java:matchesSig('org.apache.http.impl.client.HttpClients#custom()') or pmd-java:matchesSig('org.apache.http.impl.nio.client.HttpAsyncClientBuilder#create()') ] [ancestor::MethodDeclaration//ClassType[pmd-java:typeIs('org.springframework.http.HttpEntity') or pmd-java:typeIs('org.springframework.http.ResponseEntity')] ] ]]> </value> </property> </properties> <example> <![CDATA[ class Foo { ResponseEntity<Object> connectBad(Object req) { HttpEntity<Object> requestEntity = new HttpEntity<>(req); HttpClient httpClient = HttpClientBuilder.create().setMaxConnPerRoute(10).build(); // bad return remoteCall(httpClient, requestEntity); } } ]]> </example> </rule> <rule name="AvoidUnclearHttpRouteSecurity" message="HttpRoute is constructed without specifying the secure argument, this is unclear." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi24"> <description>Problem: For Apache HttpRoute, if you don't specify whether the route is secure, the default of non-secure is taken. This is unclear. If you use it to configure a connection manager for that route, and the actual route is secure, the key does not match, and the intended configuration will not be effectuated. The default number of connections (2 for Http-Client version 4, and 5 for version 5) is used which may cause requests to wait long for a connection (without a proper timeout) resulting in bad responsiveness. Solution: Always specify in the constructor whether the route is secure (true) or not (false) to make it clear. Note: Covers Apache Http-Client 4 and 5. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="io,jpinpoint-rule,performance,pitfall" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ConstructorCall[pmd-java:typeIs('org.apache.http.conn.routing.HttpRoute') or pmd-java:typeIs('org.apache.hc.client5.http.HttpRoute')]/ArgumentList[@Size <= 2] ]]></value> </property> </properties> <example> <![CDATA[ import org.apache.http.HttpHost; import org.apache.http.conn.routing.HttpRoute; import java.net.URL; public class Foo { void bar() { for (Route route : connectionProperties.routes()) { URL url = new URL(route.host()); HttpHost httpHost = new HttpHost("https", url.getHost(), getPort(url)); HttpRoute httpRouteBad1 = new HttpRoute(httpHost); // bad HttpRoute httpRouteBad2 = new HttpRoute(httpHost, null); // bad boolean secure = true; HttpRoute httpRouteGood = new HttpRoute(httpHost, null, secure); connectionManager.setMaxPerRoute(httpRouteGood, route.maxConnections()); } } } } ]]> </example> </rule> <rule name="AvoidXMLGregorianCalendar" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="XMLGregorianCalendar is used. It is slow in JAXB." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuoxar05"> <description>Problem: XMLGregorianCalendar is a large object, involving substantial processing. It is created with the poorly performing DatatypeFactory. Solution: Add a converter for alternative date handling with Java 8+ java.time. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //(FieldDeclaration|LocalVariableDeclaration|ClassLiteral|TypeArguments)/ClassType[pmd-java:typeIs('javax.xml.datatype.XMLGregorianCalendar')] | (: the excluded abundant left-overs when others are not present :) //ClassType[pmd-java:typeIs('javax.xml.datatype.XMLGregorianCalendar') and not (ancestor::ClassDeclaration//(FieldDeclaration|LocalVariableDeclaration|ClassLiteral|TypeArguments)/ClassType[pmd-java:typeIs('javax.xml.datatype.XMLGregorianCalendar')]) ] ]]></value> </property> </properties> </rule> <rule name="AxualProducerCreatedForEachMethodCall" message="An Axual (Kafka) producer is created for each method call, which is expensive and takes resources." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia10"> <description>Problem: each Producer takes threads and memory. If you create it in each method call, and call this frequently, it will result in an explosion of threads and memory used and lead to Out Of Memory Error. Solution: Since the Axual Producer is thread-safe, it should be shared e.g. from a static field. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration//LocalVariableDeclaration/ClassType[pmd-java:typeIs('io.axual.client.producer.Producer')] [ancestor::ClassDeclaration[not(pmd-java:hasAnnotation('org.springframework.context.annotation.Configuration'))]] ]]></value> </property> </properties> <example> <![CDATA[ import io.axual.client.producer.Producer; public class AxualProducerBad { public void publishToEventStream() { Producer<String, String> producer = axualClient.buildProducer(producerConfig); // bad producer.produce(msg); } } class AxualProducerGood1 { private static final Producer<String, String> producer = AxualClient.buildProducer(producerConfig); } @Configuration class AxualProducerGood2 { public Producer<String, String> axualProducer() { Producer<String, String> producer = axualClient.buildProducer(producerConfig); return producer; } } ]]> </example> </rule> <rule name="BufferingClientHttpRequestFactoryIsMemoryGreedy" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="BufferingClientHttpRequestFactory is memory greedy. Only use when multiple reads of the response body cannot be avoided." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi18"> <description>org.springframework.http.client.BufferingClientHttpRequestFactory is used. Problem: It buffers all incoming and outgoing streams fully in memory which may result in high memory usage. Solution: Avoid multiple reads of the response body, so it is not needed. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,memory,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ConstructorCall[pmd-java:typeIs('org.springframework.http.client.BufferingClientHttpRequestFactory')] ]]> </value> </property> </properties> <example> <![CDATA[ import org.springframework.http.client.*; import org.springframework.web.client.RestTemplate; public class Foo { public RestTemplate createMemoryGreedyRestTemplate(HttpClientConfiguration httpClientConfiguration) { ClientHttpRequestFactory factory = getClientHttpRequestFactory(httpClientConfiguration); return new RestTemplate(new BufferingClientHttpRequestFactory(factory)); // bad } public RestTemplate createStreamTroughRestTemplate(HttpClientConfiguration httpClientConfiguration) { ClientHttpRequestFactory factory = getClientHttpRequestFactory(httpClientConfiguration); return new RestTemplate(factory); // good } } ]]> </example> </rule> <rule name="DefaultFeignClientWithoutTLSConnectionReuse" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Default Feign client is used with (mutual) TLS. This is HttpURLConnection that creates a new connection for each call with TLS overhead." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi21"> <description>Problem: the default http client of Feign is java.net.HttpURLConnection that does not pool connections when using mutual TLS. This causes connection handshake overhead: extra CPU usage and higher latency. Solution: switch to a Feign client that supports HTTP connection pooling with mTLS, for instance Apache HttpClient 4 with disableConnectionState and proper connection pool size and timeouts. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: default client constructor with sslSocketFactory :) //MethodDeclaration//ConstructorCall[pmd-java:typeIs('feign.Client.Default') and ArgumentList[@Size=2]/*[1][not(self::NullLiteral)]] ]]> </value> </property> </properties> <example> <![CDATA[ import feign.Client; public class MyFeignClient { public Client createClient(SSLSocketFactory sslSocketFactory){ return new Client.Default(sslSocketFactory, null); // bad } public Client createClient(){ return new Client.Default(null, null); // good } public Feign.Builder feignBuilder() { Client feignClient = new Client.Default(setupSSLContextForMutualTLS().getSocketFactory(), new DefaultHostnameVerifier()); // bad return Feign.builder().client(feignClient); } } ]]> </example> </rule> <rule name="GsonCreatedForEachMethodCall" message="A Gson object is created for each method call, which is expensive." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/master/docs/JavaCodePerformance.md#IUOJAR03"> <description>Problem: Gson creation is relatively expensive. A JMH benchmark shows a 24x improvement reusing one instance. Solution: Since Gson objects are thread-safe after creation, they can be between threads. So, reuse created instances, from a static field. Pay attention to use thread-safe (custom) adapters and serializers. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassDeclaration[not (pmd-java:hasAnnotation('org.springframework.context.annotation.Configuration'))]//MethodDeclaration/( .//ConstructorCall[pmd-java:typeIs('com.google.gson.Gson')]| .//MethodCall[pmd-java:matchesSig('com.google.gson.GsonBuilder#create()')]//(ConstructorCall|VariableAccess)[pmd-java:typeIs('com.google.gson.GsonBuilder')] ) ]]></value> </property> </properties> <example> <![CDATA[ class Bad { public String toJson(User user) { return new Gson().toJson(user); // bad, new Gson() for each call } } class Good { private static final Gson GSON = new GsonBuilder().serializeNulls().create(); // good public String toJson(User user) { return GSON.toJson(user); // good, reuse GSON } } ]]> </example> </rule> <rule name="HttpClientBuilderPoolSettingsIgnored" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="HttpClient builder is used with a ConnectionManager and MaxConn settings on the client are ignored. Remove them." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi09"> <description>Problem: If you use setConnectionManager, the connection pool must be configured on that Connection Manager. Pool settings on the client are ignored and lost. Solution: HttpClients should either 1. use setConnectionManager and *only* call setMaxTotal and setDefaultMaxPerRoute on that ConnectionManager or 2. not use a ConnectionManager and call setMaxConnTotal and setMaxConnPerRoute on the client directly (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="correctness,jpinpoint-rule,resilience" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration[ .//MethodCall/(TypeExpression|VariableAccess)[ pmd-java:typeIs('org.apache.http.impl.client.HttpClientBuilder') or pmd-java:typeIs('org.apache.http.impl.client.HttpClients') or pmd-java:typeIs('org.apache.http.impl.nio.client.HttpAsyncClientBuilder') ] ][ .//MethodCall[@MethodName='setConnectionManager'] ] (: trying to get to the right line, unfortunately, the chaining seems to make it not work :) //MethodCall[@MethodName=('setMaxConnTotal','setMaxConnPerRoute')] ]]> </value> </property> </properties> <example> <![CDATA[ return HttpClientBuilder.create() .setConnectionManager(conMgr) .setMaxConnPerRoute(MAX_CONNECTIONS_TOTAL) // bad, ignored .build(); return HttpClientBuilder.create() // good .setMaxConnPerRoute(MAX_CONNECTIONS_TOTAL) .setMaxConnTotal(MAX_CONNECTIONS_TOTAL) .build(); return HttpClientBuilder.create() // good .setConnectionManager(conMgr) .build(); ]]> </example> </rule> <rule name="HttpClientBuilderWithoutDisableConnectionState" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="A HttpClient builder is used and disableConnectionState is not called. HTTP client tracks connection state while using TLS" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi07"> <description>Problem: NTLM authenticated connections and SSL/TLS connections with client certificate authentication are stateful: they have a specific user identity/security context per session. If HttpClients have enabled connection state tracking which is the default, established TLS connections will not be reused because it is assumed that the user identity or security context may differ. Then performance will suffer due to a full TLS handshake for each request. Solution: HttpClients should disable connection state tracking in order to reuse TLS connections, since service calls for one pool have the same user identity/security context for all sessions. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodCall[ pmd-java:matchesSig('org.apache.http.impl.client.HttpClientBuilder#create()') or pmd-java:matchesSig('org.apache.http.impl.client.HttpClients#custom()') or pmd-java:matchesSig('org.apache.http.impl.nio.client.HttpAsyncClientBuilder#build()') ][ not(ancestor::MethodDeclaration//MethodCall[@MethodName='disableConnectionState']) ] ]]> </value> </property> </properties> <example> <![CDATA[ class Bad { public HttpClient bad(ConnectionManager conMgr) { HttpClient client = HttpClientBuilder.create() .setConnectionManager(conMgr) .build(); // bad, disableConnectionState is missing return client; } } class Good { public HttpClient good(ConnectionManager conMgr) { return HttpClientBuilder.create() .setConnectionManager(conMgr) .disableConnectionState() // good .build(); } } ]]> </example> </rule> <rule name="HttpClientBuilderWithoutPoolSize" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="HttpClient builder is used and the connection pool is not configured. HttpClient defaults of 2 per route and 20 max total are probably not right." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi03"> <description>Problem: by default only 2 connections are available per route. When two external calls are being made, a third thread requesting a connection must wait for a free connection. Solution: HttpClients should explicitly define the number of connections per route. If only one route is used, make the max number of connections equal to conn per route. Either use 1. setConnectionManager and call setMaxTotal and setDefaultMaxPerRoute on that connection manager, or 2. no ConnectionManager: call setMaxConnTotal and setMaxConnPerRoute on the client directly. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,performance,resilience" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration //MethodCall[@MethodName!='build']/(TypeExpression|VariableAccess)[ pmd-java:typeIs('org.apache.http.impl.client.HttpClientBuilder') or pmd-java:typeIs('org.apache.http.impl.client.HttpClients') or pmd-java:typeIs('org.apache.http.impl.nio.client.HttpAsyncClientBuilder') ][ ancestor::MethodDeclaration[ not( .//MethodCall[@MethodName='setConnectionManager'] or (.//MethodCall[@MethodName='setMaxConnTotal'] and .//MethodCall[@MethodName='setMaxConnPerRoute']) ) ] ] , //MethodDeclaration[ClassType[@SimpleName='PoolingHttpClientConnectionManager']] /(Block|FormalParameters)//ClassType[pmd-java:typeIs('org.apache.http.impl.conn.PoolingHttpClientConnectionManager')] [ ancestor::MethodDeclaration[ not( .//MethodCall[@MethodName='setConnectionManager'] or (.//MethodCall[@MethodName='setMaxTotal'] and .//MethodCall[@MethodName='setDefaultMaxPerRoute']) ) ] ] ]]> </value> </property> </properties> <example> <![CDATA[ return HttpClientBuilder.create() // bad, missing setMaxConnPerRoute/setMaxConnTotal or setConnectionManager .disableConnectionState() .build(); return HttpClientBuilder.create() // good, both setMaxConnPerRoute and setMaxConnTotal called on client .setMaxConnPerRoute(MAX_CONNECTIONS_TOTAL) .setMaxConnTotal(MAX_CONNECTIONS_TOTAL) .build(); return HttpClientBuilder.create() // good, setConnectionManager called, pool config delegated .setConnectionManager(conMgr) .build(); ]]> </example> </rule> <rule name="HttpClientBuilderWithoutTimeouts" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="HttpClient-v4 builder is used and not all three timeouts are configured. The defaults are probably not optimal (e.g. infinite). " externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#IBI10"> <description>Problem: (Apache client version 4) For connectionRequestTimeout, connectTimeout, socketTimeout (for HttpComponentsClientHttpRequestFactory) or readTimeout (for RequestConfig) the default timeout settings are not optimal in most cases. Solution: Set the timeouts explicitly to proper reasoned values. See best practice values via the link. Use the setDefaultRequestConfig with a method with a RequestConfig object on HttpClient builders to set the timeouts.(jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="io,jpinpoint-rule,performance,pitfall,resilience" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration //MethodCall/(TypeExpression|VariableAccess)[ pmd-java:typeIs('org.apache.http.impl.client.HttpClientBuilder') or pmd-java:typeIs('org.apache.http.impl.client.HttpClients') or pmd-java:typeIs('org.apache.http.impl.nio.client.HttpAsyncClientBuilder') ][ ancestor::MethodDeclaration[ not( .//MethodCall[@MethodName='setDefaultRequestConfig'] or ( ancestor::ClassBody//MethodCall[@MethodName='setRequestConfigCallback'] and ancestor::ClassBody/MethodDeclaration/ClassType[@SimpleName='Builder']/ClassType[@SimpleName='RequestConfig']/../.. [count(.//MethodCall[@MethodName=('setConnectionRequestTimeout','setConnectTimeout','setSocketTimeout')]) = 3] ) ) ] ] , //MethodDeclaration//MethodCall/(TypeExpression|VariableAccess)[pmd-java:typeIs('org.apache.http.client.config.RequestConfig')] [ancestor::MethodDeclaration//MethodCall[@MethodName='custom']] [ancestor::MethodDeclaration[count(.//MethodCall[@MethodName=('setConnectionRequestTimeout','setConnectTimeout','setSocketTimeout')]) != 3]] , (: only client v4 :) //ImportDeclaration[starts-with(@PackageName, 'org.apache.http.')]/.. //MethodDeclaration//( ConstructorCall[not( .//MethodCall[pmd-java:typeIs('org.apache.http.client.HttpClient')] )] |FormalParameter )[pmd-java:typeIs('org.springframework.http.client.HttpComponentsClientHttpRequestFactory')] [not( (.//VariableAccess[pmd-java:typeIs('org.apache.http.client.HttpClient')] or ancestor::MethodDeclaration//MethodCall[@MethodName='setHttpClient']) and ancestor::MethodDeclaration[ count(.//MethodCall[@MethodName=('setConnectionRequestTimeout','setConnectTimeout','setReadTimeout')]/VariableAccess[ pmd-java:typeIs('org.springframework.http.client.HttpComponentsClientHttpRequestFactory') ]) = 0 ] )] [ancestor::MethodDeclaration[count(.//MethodCall[@MethodName=('setConnectionRequestTimeout','setConnectTimeout','setReadTimeout')]) != 3]] ]]> </value> </property> </properties> <example> <![CDATA[ RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(4000) .build(); // bad, not all timeouts set return HttpClientBuilder.create() .setDefaultRequestConfig(requestConfig) // good .build(); ]]> </example> </rule> <rule name="HttpClientImproperConnectionTimeouts" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="ApacheHttpClient RequestConfig connectionRequestTimeout and connectTimeout have values which are typically too high." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi20"> <description>org.apache.http.client.config.RequestConfig is used with connectionRequestTimeout and connectTimeout values above 500 milli seconds. Problem: 1. connectTimeout is for establishing a connection which should be quick, say below 200 ms. 2. connectionRequestTimeout is for requesting a connection from the connection manager, which should be almost as quick, say below 250 ms. If timeouts are long, requests will wait long for an unavailable service and cause high thread usage and possibly overload. Solution: Set connectTimeout and connectionRequestTimeout to values based om tests, for instance 200 ms and 250 ms. respectively (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="io,jpinpoint-rule,performance,sustainability-low,resilience" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: use of field, local var and return statement :) //FieldDeclaration[PrimitiveType[@Kind='int' or @Kind='long']][.//VariableId[ @Name = ancestor::ClassDeclaration[ .//LocalVariableDeclaration/ClassType[ pmd-java:typeIs('org.apache.http.client.config.RequestConfig') or pmd-java:typeIs('org.springframework.http.client.HttpComponentsClientHttpRequestFactory') ] /..//VariableDeclarator |.//ReturnStatement[ .//MethodCall[@MethodName='custom']/TypeExpression/ClassType[ pmd-java:typeIs('org.apache.http.client.config.RequestConfig') or pmd-java:typeIs('org.springframework.http.client.HttpComponentsClientHttpRequestFactory') ] ] ] //MethodCall[@MethodName=('setConnectionRequestTimeout','setConnectTimeout')] /ArgumentList/VariableAccess/@Name ]] /(VariableDeclarator//NumericLiteral[@ValueAsLong > 500] |.//Annotation[@SimpleName='Value']//StringLiteral[number(substring-before(substring-after(@Image, ':'), '}')) > 500] ) , (: use of literal, local var and return statement : :) //Block[ .//LocalVariableDeclaration/ClassType[ pmd-java:typeIs('org.apache.http.client.config.RequestConfig') or pmd-java:typeIs('org.springframework.http.client.HttpComponentsClientHttpRequestFactory') ] /..//VariableDeclarator |.//ReturnStatement[ .//MethodCall[@MethodName='custom']/TypeExpression/ClassType[ pmd-java:typeIs('org.apache.http.client.config.RequestConfig') or pmd-java:typeIs('org.springframework.http.client.HttpComponentsClientHttpRequestFactory') ] ] ] //MethodCall[@MethodName=('setConnectionRequestTimeout','setConnectTimeout')] /ArgumentList/NumericLiteral[@ValueAsLong > 500] ]]> </value> </property> </properties> <example> <![CDATA[ import org.apache.http.client.config.RequestConfig; public class HttpClientStuff { private static final int CONNECTION_TIMEOUTMILLIS = 1000; // bad // timeout until a connection is established private static final int CONNECTIONREQUEST_TIMEOUTMILLIS = 5000; // bad // timeout when requesting a connection from the connection manager private static final int SOCKET_TIMEOUTMILLIS = 5000; // timeout of waiting for data public RequestConfig requestConfigWithTimeouts() { RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(CONNECTIONREQUEST_TIMEOUTMILLIS) .setConnectTimeout(CONNECTION_TIMEOUTMILLIS) .setSocketTimeout(SOCKET_TIMEOUTMILLIS) .build(); return requestConfig; } } ]]> </example> </rule> <rule name="JAXBContextCreatedForEachMethodCall" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="JAXBContext is created for each method call, which is expensive." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#iuoxar04"> <description>Problem: JAXBContext creation is expensive because it does much class loading. Solution: Since JAXBContext objects are thread safe, they can be shared between requests and reused. So, reuse created instances, e.g. as singletons. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration[not(pmd-java:hasAnnotation('javax.annotation.PostConstruct') or pmd-java:hasAnnotation('jakarta.annotation.PostConstruct'))] //MethodCall[pmd-java:matchesSig('javax.xml.bind.JAXBContext#newInstance(_*)') or pmd-java:matchesSig('jakarta.xml.bind.JAXBContext#newInstance(_*)')] ]]></value> </property> </properties> </rule> <rule name="ObjectMapperCreatedForEachMethodCall" message="An ObjectMapper or JsonMapper is created for each method call, which is expensive." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#IUOJAR01"> <description>Problem: Jackson ObjectMapper/JsonMapper creation is expensive because it does much class loading. Solution: Since ObjectMapper/JsonMapper objects are thread-safe after configuration in one thread, they can be shared afterward between requests and reused. So, reuse created instances, from a static field. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: ObjectMapper constructor called in a method :) //MethodDeclaration//ConstructorCall[(pmd-java:typeIs('com.fasterxml.jackson.databind.ObjectMapper')) and (: configuration is assumed to be called only once :) not(ancestor::ClassDeclaration[1][(pmd-java:hasAnnotation('org.springframework.context.annotation.Configuration'))]) ], (: a violation for both builder() and build() method because both can be expensive :) //MethodDeclaration//MethodCall[(pmd-java:matchesSig('com.fasterxml.jackson.databind.json.JsonMapper#builder()') or pmd-java:matchesSig('com.fasterxml.jackson.databind.cfg.MapperBuilder#build()')) and (: configuration is assumed to be called only once :) not(ancestor::ClassDeclaration[1][(pmd-java:hasAnnotation('org.springframework.context.annotation.Configuration'))]) ] ]]></value> </property> </properties> <example> <![CDATA[ public static ObjectMapper createMapper() { return new ObjectMapper(); // bad } public static JsonMapper createMapper() { return JsonMapper.builder().build(); // bad } ]]> </example> </rule> <rule name="RetryCanCauseOverload" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Be careful with retrying: multiple retry locations in a call chain can cause overload." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi13"> <description>Problem: Multiple Retry locations in a call chain multiply the number of calls. For 2x retry on 3 locations (service calls) in a chain calling a system which is just recovering, results in 3 x 3 x 3 = 27 calls instead of 1. This may cause it not being able to restart. Solution: Have the retry mechanism in one location in the chain only, recommended only the one closest to the user. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>5</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,resilience,suspicious,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //Annotation[@SimpleName='Retry'] , //Annotation[@SimpleName='Retryable'] , //FieldDeclaration/ClassType[pmd-java:typeIs('io.github.resilience4j.retry.Retry')] ]]> </value> </property> </properties> <example> <![CDATA[ import io.github.resilience4j.retry.annotation.Retry; @Retry(name = "some-service") // inform public class Foo { public Response callSomeService() { //...and someService does a Retry for a call to the next service } } ]]> </example> </rule> <rule name="SetQueueCapacityForTaskExecutor" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="Configure the queue capacity for the ThreadPoolTaskExecutor." externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ia05"> <description>The org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor queue capacity is not configured. Problem: It has a default queue capacity which is unlimited which can lead to an out of memory situation. Solution: Call setQueueCapacity, for instance with a value equal to CorePoolSize. Note that the pool will only grow beyond CorePoolSize up to MaxPoolSize when the queue is full. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="bad-practice,jpinpoint-rule,memory,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //LocalVariableDeclaration//ConstructorCall[pmd-java:typeIs('org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor')] [ancestor::MethodDeclaration[.//MethodCall[@MethodName=('setCorePoolSize','setMaxPoolSize')] and not(.//MethodCall[@MethodName='setQueueCapacity'])]] ]]> </value> </property> </properties> <example> <![CDATA[ private ThreadPoolTaskExecutor bad() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // bad executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.initialize(); return executor; } private ThreadPoolTaskExecutor good() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setQueueCapacity(10); // good executor.setMaxPoolSize(20); executor.initialize(); return executor; } ]]> </example> </rule> <rule name="UseCloseableForHttpClient" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" message="HttpClient is used instead of ClosableHttpClient. " externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ibi08"> <description>Problem: if HttpClient connections are not closed properly when needed, resources are not released and connections may not (or not quick enough) become available from the pool. Solution: Use ClosableHttpClient to allow for invoking close on it to properly close the connection. Or use HttpComponentsClientHttpRequestFactory(httpClient) to let it manage closing. (jpinpoint-rules) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,performance" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ (: bad: HttpClient without HttpComponentsClientHttpRequestFactory :) //LocalVariableDeclaration/ClassType[pmd-java:typeIsExactly('org.apache.http.client.HttpClient') and not(ancestor::MethodDeclaration//ConstructorCall [pmd-java:typeIs('org.springframework.http.client.HttpComponentsClientHttpRequestFactory')])] , (: bad: HttpClient with HttpComponentsClientHttpRequestFactory, yet if passed a param of constructor it is good :) //LocalVariableDeclaration/ClassType[pmd-java:typeIsExactly('org.apache.http.client.HttpClient')] [ancestor::MethodDeclaration//ConstructorCall [pmd-java:typeIs('org.springframework.http.client.HttpComponentsClientHttpRequestFactory') and not(ArgumentList//VariableAccess[pmd-java:typeIs('org.apache.http.client.HttpClient')]) and not(ancestor::MethodDeclaration//MethodCall[@MethodName = 'setHttpClient'])]] ]]> </value> </property> </properties> <example> <![CDATA[ void bad() { HttpClient httpClient = HttpClientBuilder.create() .disableConnectionState().build(); } void good() { CloseableHttpClient httpClient = HttpClientBuilder.create() .disableConnectionState().build(); } void good2() { HttpClient httpClient = HttpClientBuilder.create().disableConnectionState().build(); ClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); } ]]> </example> </rule> <!-- END Included file 'remoting.xml' --> <!-- BEGIN Included file 'spring.xml' --> <rule name="AvoidExpressionsInCacheable" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Avoid SpEL-expression for computing Cacheable key" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#improper-caching"> <description>Spring Expression Language (SpEL) expression is used for computing the key dynamically. Problem: evaluating the expression language is expensive, on every call. Solution: use a custom KeyGenerator: keyGenerator=... instead of key=... (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //Annotation[pmd-java:typeIs('org.springframework.cache.annotation.Cacheable')]/AnnotationMemberList/MemberValuePair[@Name='key'] ]]></value> </property> </properties> <example> <![CDATA[ class Bad { @Cacheable(value = "Cache1", key = "#key1") // bad public String bad(final String key1) { return getRemote(key1); } } class Good { @Cacheable(value = "Cache1", keyGenerator = "keyGen") // good public String good(final String key1) { return getRemote(key1); } } ]]> </example> </rule> <rule name="AvoidIdentityCacheKeys" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Avoid identity cache keys by casting the generate method parameters" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ic13"> <description>A non-overridden Object.toString may be called on a spring KeyGenerator.generate method parameter. Problem: The non-overridden Object.toString returns a String representing the identity of the object. Because this is different for two objects with the same value, cache keys will be different and the cache will only have misses and no hits. Solution: Cast the parameters each to the type used at call site and also check the expected number of params. Or better: return a SimpleKey composed typically of class and method name and the params. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="bad-practice,cpu,jpinpoint-rule,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ImplementsList/ClassType[pmd-java:typeIs("org.springframework.cache.interceptor.KeyGenerator")] /../..//MethodDeclaration[@Name='generate'] (: select all variables in block, *[2] to select part after assignment :) /Block// (ReturnStatement|LocalVariableDeclaration/VariableDeclarator/*[2]) (: where name matches the third Object... parameter name of generate method :) //VariableAccess[ @Name = ancestor::MethodDeclaration//FormalParameter[3]/VariableId/@Name and not( (: without an explicit cast :) exists(../../..//CastExpression) (: - exclude if in an if-block checking params.length to do error logging or so - :) or exists(ancestor::IfStatement//FieldAccess[@Name = 'length']) ) ] , //ImplementsList/ClassType[pmd-java:typeIs("org.springframework.cache.interceptor.KeyGenerator")] /../..//MethodDeclaration[@Name='generate'] //ForeachStatement[ VariableAccess[@Name = ancestor::MethodDeclaration//FormalParameter[3]/VariableId/@Name] ] //Block//VariableAccess[ @Name = ancestor::ForeachStatement/LocalVariableDeclaration//VariableId/@Name and ../../MethodCall[@MethodName = 'toString'] ] ]]></value> </property> </properties> <example> <![CDATA[ import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.util.StringUtils; public class Bad implements KeyGenerator { public Object generate(Object target, Method method, Object... params) { List<Object> objArray = Arrays.asList(params); return target.getClass().getName() + "_" + method.getName() + "_" + StringUtils.arrayToDelimitedString(params, "_"); // bad, do not concatenate without casting } } public class Good implements KeyGenerator { public Object generate(Object target, Method method, Object... params) { if (params.length != 1) { throw new IllegalArgumentException("KeyGenerator for GetProfileCache assumes 1 parameter 'profileId', found: " + params); } String profileId = (String) params[0]; // good: includes cast return profileId; } } ]]> </example> </rule> <rule name="AvoidImproperAnnotationCombinations" language="java" message="Don't combine these annotations" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodeQuality.md#ssc02" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> <description> Improper combination of annotations. Problem: these annotations are not meant to be combined and may cause unexpected and unwanted behavior, e.g. data mix-up. Solution: remove the inappropriate annotation. Don't combine 2+ of [@Component, @Service, @Configuration, @Controller, @RestController, @Repository, @Entity] (Spring/JPA) Don't combine @Aspect with one of [@Service, @Configuration, @Controller, @RestController, @Repository, @Entity] (Spring/AspectJ) Don't combine [@Data with @Value] and [@Data or @Value] with any of [@ToString, @EqualsHashCode, @Getter, @Setter, @RequiredArgsConstructor] (Lombok) Don't combine @Data with any of [@Component, @Service, @Controller, @RestController, @Repository], it may cause user data mix-up. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="confusing,data-mix-up,jpinpoint-rule,suspicious" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //ClassDeclaration/ModifierList[count(Annotation[pmd-java:typeIs('org.springframework.stereotype.Controller') or pmd-java:typeIs('org.springframework.stereotype.Service') or pmd-java:typeIs('org.springframework.stereotype.Component') or pmd-java:typeIs('org.springframework.stereotype.Repository') or pmd-java:typeIs('org.springframework.context.annotation.Configuration') or pmd-java:typeIs('org.springframework.web.bind.annotation.RestController') or pmd-java:typeIs('javax.persistence.Entity') or pmd-java:typeIs('jakarta.persistence.Entity')]) > 1 ]/Annotation[2] , //ClassDeclaration/ModifierList[ Annotation[pmd-java:typeIs('org.aspectj.lang.annotation.Aspect')] and count(Annotation[pmd-java:typeIs('org.springframework.stereotype.Controller') or pmd-java:typeIs('org.springframework.stereotype.Service') or pmd-java:typeIs('org.springframework.stereotype.Repository') or pmd-java:typeIs('org.springframework.context.annotation.Configuration') or pmd-java:typeIs('org.springframework.web.bind.annotation.RestController') or pmd-java:typeIs('javax.persistence.Entity') or pmd-java:typeIs('jakarta.persistence.Entity')]) > 0 ]/Annotation[2] , //ClassDeclaration/ModifierList[ count(Annotation[pmd-java:typeIs('lombok.Data') or pmd-java:typeIs('lombok.Value')]) > 1 ]/Annotation[2] , //ClassDeclaration/ModifierList[ Annotation[pmd-java:typeIs('lombok.Data') or pmd-java:typeIs('lombok.Value')] and Annotation[(pmd-java:typeIs('lombok.ToString') or pmd-java:typeIs('lombok.EqualsAndHashCode') or pmd-java:typeIs('lombok.Getter') or pmd-java:typeIs('lombok.Setter') or pmd-java:typeIs('lombok.RequiredArgsConstructor')) and not (AnnotationMemberList)] ]/Annotation[2] , //ClassDeclaration/ModifierList[ Annotation[pmd-java:typeIs('lombok.Data')] and Annotation[pmd-java:typeIs('org.springframework.stereotype.Controller') or pmd-java:typeIs('org.springframework.stereotype.Service') or pmd-java:typeIs('org.springframework.stereotype.Component') or pmd-java:typeIs('org.springframework.stereotype.Repository') or pmd-java:typeIs('org.springframework.web.bind.annotation.RestController')] ]/Annotation[not(pmd-java:typeIs('lombok.Data'))][1] ]]> </value> </property> </properties> <example> <![CDATA[ @Component @Entity // bad class Bad { } @Component class Good { } ]]> </example> </rule> <rule name="AvoidModelMapAsRenderParameter" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="A ModelMap or @ModelAttribute is used as parameter of a portlet render method and implicitly put in the session." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tmsu11"> <description>Problem: ModelMaps are rather large objects containing explicitly added data and administrative data from Spring. They are added to the Portlet session implicitly. They stay in the session for some time: during session activity and 30 minutes (HTTP timeout) after it, in case the user does not exit explicitly. They occupy heap space during that time, for every user. Solution: Remove the ModelMap from the render method parameter list and create a new local ModelMap to use in the render request scope. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,memory,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration[ pmd-java:modifiers() = 'public' and .//FormalParameter[ pmd-java:typeIs('org.springframework.ui.ModelMap') or pmd-java:hasAnnotation('org.springframework.web.bind.annotation.ModelAttribute')] and .//FormalParameter[ (pmd-java:typeIs('javax.portlet.RenderRequest') or pmd-java:typeIs('javax.portlet.PortletRequest')) or pmd-java:hasAnnotation('org.springframework.web.portlet.bind.annotation.RenderMapping') ] ] ]]></value> </property> </properties> <example><![CDATA[ // bad: ModelMap in session between action and render, and after that @RenderMapping(params = "flow=view_unsigned") public ModelAndView viewDoodle(RenderRequest request, @RequestParam String doodleId, @RequestParam String modifiedTimeStamp, ModelMap modelMap) { ... return new ModelAndView("FIRST_SCREEN_VIEW", "FIRST_SCREEN_FORM", modelMap); } // good: assumed not in session public ModelAndView initialOverviewRender(RenderRequest request, String viewName) { ModelMap modelMap = new ModelMap(); modelMap.put("texts", messageSource); // ModelMap used during render, in view template. Assumed not to be put // in session, because no need. return new ModelAndView("overview/" + viewName, modelMap); } ]]></example> </rule> <rule name="AvoidSimpleCaches" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Avoid simple caching in production" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ic08"> <description>Simple caches are used. Problem: Simple caching is meant for testing and prototyping plus it lacks manageability and monitorability. Solution: Use a proper cache implementation like ehcache or a cloud cache. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="bad-practice,jpinpoint-rule,performance" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ConstructorCall[ pmd-java:typeIs('org.springframework.cache.support.SimpleCacheManager') or pmd-java:typeIs('org.springframework.cache.concurrent.ConcurrentMapCache') ] ]]></value> </property> </properties> <example> <![CDATA[ @EnableCaching @Configuration class Bad { @Bean public CacheManager cacheManager() { return new SimpleCacheManager().setCaches(Arrays.asList(new ConcurrentMapCache("ourCache"))); // bad } } @EnableCaching @Configuration class Good { @Bean public CacheManager cacheManager() { return new EhCacheCacheManager(ehCacheCacheManagerFactory().getObject()); // good } } ]]> </example> </rule> <rule name="AvoidSimpleKeyCollisions" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Generate a unique SimpleKey by using both method and parameters as composites." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ic15"> <description> Problem: Spring's SimpleKey creation lacks either the method or the method parameters, which may cause cache data mix-up. Solution: Create a SimpleKey composed of both the method object and the params Object[]. Make sure the params properly implement equals and hashCode. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="bad-practice,data-mix-up,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ImplementsList/ClassType[pmd-java:typeIs("org.springframework.cache.interceptor.KeyGenerator")] /../..//MethodDeclaration[@Name='generate'] //(ConstructorCall|MethodCall)[ pmd-java:matchesSig('org.springframework.cache.interceptor.SimpleKey#new(_)') or pmd-java:matchesSig('org.springframework.cache.interceptor.SimpleKeyGenerator#generateKey(_)') (: and parameter of constructor is a direct use of one of method params :) and not( (: 'method' param is used :) ./ArgumentList/VariableAccess[@Name = ancestor::MethodDeclaration//FormalParameter[2]/VariableId/@Name] and (: 'params' param is used :) ./ArgumentList/VariableAccess[@Name = ancestor::MethodDeclaration//FormalParameter[3]/VariableId/@Name]) ] ]]></value> </property> </properties> <example> <![CDATA[ import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleKey; import java.lang.reflect.Method; class BadCacheKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { return new SimpleKey(params); // bad } } class GoodCacheKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { return new SimpleKey(method, params); // good } } ]]> </example> </rule> <rule name="AvoidSpringApplicationContextRecreation" message="Avoid re-creation of Spring application context" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#euocs01"> <description> Problem: When a XXXApplicationContext is created, all Spring beans are initialized, wired and component scanning may take place. Component scanning involves extensive class path scanning which is expensive. Solution: Create the ApplicationContext only once in the application deployed/live time. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //MethodDeclaration//ConstructorCall[ (: assume creation of all ApplicationContexts is bad in methods - does not work in designer without proper classpath imports :) ClassType[pmd-java:typeIs('org.springframework.context.ApplicationContext')] ] ]]></value> </property> </properties> <example><![CDATA[ public class AvoidSpringApplicationContextRecreation { private static final ApplicationContext APPLICATION_CONTEXT = new ClassPathXmlApplicationContext(new String[]{ "t-spring-context.xml" }); // good private Object getServiceBad() { final ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"t-spring-context.xml"}); // bad return (Object) applicationContext.getBean("T_SERVICE"); } private Object getServiceGood() { return (Object) APPLICATION_CONTEXT.getBean("T_SERVICE"); } } ]]></example> </rule> <rule name="AvoidSpringMVCMemoryLeaks" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Spring Controller returns an additive expression or a ModelAndView object which may cause a MemoryLeak" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md"> <description>Avoid to return an additive expression for a Spring Controller because it may cause a MemoryLeak. Each new value returned will create a new entry in the View Cache. Also avoid to return a ModelAndView object created using non-static and non-final methods because it may cause a MemoryLeak. Solution: Although multiple solutions exist you can make use of model attributes together with a redirectUrl, example: redirect:/redirectUrl?someAttribute={someAttribute}.(jpinpoint-rules)</description> <priority>1</priority> <properties> <property name="tags" value="jpinpoint-rule,memory,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ReturnStatement//InfixExpression[@Operator = "+"] [ancestor::MethodDeclaration[pmd-java:hasAnnotation('org.springframework.web.bind.annotation.RequestMapping')]] [ancestor::MethodDeclaration [.//FormalParameter[pmd-java:hasAnnotation('org.springframework.web.bind.annotation.RequestParam')]] [.//FormalParameter[pmd-java:typeIs('org.springframework.ui.Model')]]] , //MethodCall[pmd-java:matchesSig('org.springframework.web.servlet.ModelAndView#setViewName(_*)')] [ (: no non-final vars or method local vars and params :) .//ArgumentList//VariableAccess[ @Name = ancestor::MethodDeclaration//(VariableDeclarator|FormalParameter)/VariableId/@Name or @Name = ancestor::ClassDeclaration//FieldDeclaration[not(pmd-java:modifiers()='final')]//VariableId/@Name ] ] , //ConstructorCall[pmd-java:typeIs('org.springframework.web.servlet.ModelAndView')] [ (: in constructor, only match first arg :) .//ArgumentList/*[1][ @Name = ancestor::MethodDeclaration//(VariableDeclarator|FormalParameter)/VariableId/@Name or @Name = ancestor::ClassDeclaration//FieldDeclaration[not(pmd-java:modifiers()='final')]//VariableId/@Name ] or (: but in first arg also check sub-args of e.g. method calls :) .//ArgumentList/*[1]//ArgumentList/VariableAccess[ @Name = ancestor::MethodDeclaration//(VariableDeclarator|FormalParameter)/VariableId/@Name or @Name = ancestor::ClassDeclaration//FieldDeclaration[not(pmd-java:modifiers()='final')]//VariableId/@Name ] ] ]]></value> </property> </properties> <example><![CDATA[ @RequestMapping(method = RequestMethod.GET) public ModelAndView getSomeRequestBad(@RequestParam int someValue) { ... return new ModelAndView(someVar); // bad } @RequestMapping(method = RequestMethod.GET) public ModelAndView getSomeRequestGood(@RequestParam int someValue) { ... return new ModelAndView(STATIC_FINAL_FIELD); // good } ]]></example> </rule> <rule name="EnsureProperCacheableParams" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Info: Make sure that the parameters that make up the cache key implement the required methods properly." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ic16"> <description> Problem: When (1) concatenating or joining parameters in a KeyGenerator: they need to properly implement toString(). (2) using SimpleKey (recommended): the parameters need to properly implement equals() and hashCode(). Failing to do so may lead to caching data mix-up. Solution: Create a SimpleKey composed of both the method object and the params Object[] and make sure the params properly implement equals and hashCode. Note: This rule is just informational, because it cannot actually check if it is implemented correctly or not. (jpinpoint-rules)</description> <priority>5</priority> <properties> <property name="tags" value="data-mix-up,jpinpoint-rule,suspicious" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration[pmd-java:hasAnnotation('org.springframework.cache.annotation.Cacheable')] //FormalParameter/ClassType [(: common safe reference types :) not (pmd-java:typeIs('java.lang.String') or pmd-java:typeIs('java.lang.Integer') or pmd-java:typeIs('java.time.LocalDate') or pmd-java:typeIs('org.joda.time.LocalDate') ) ] ]]></value> </property> </properties> <example> <![CDATA[ import org.springframework.cache.annotation.Cacheable; import java.time.*; import java.lang.*; class Foo { @Cacheable(value = "myCache", keyGenerator = "myGenerator") public String getDataGood(String str, LocalDate date) { return service.getData(input); } @Cacheable(value = "myCache", keyGenerator = "myGenerator") public String getDataInform(MyObject input, String str, LocalDate date) { // inform return service.getData(input); } } class MyObject { String field; // equals, hashCode, toString missing } ]]> </example> </rule> <rule name="MakeAutoWiredConstructedFieldFinal" message="Make autowired, constructed field final in objects shared among threads." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tutc07"> <description> Problem: Multiple threads typically access fields of a singleton or may access fields in session scoped objects. If a field or its reference is mutable, non-autowired access is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. Solution: Make the fields final and unmodifiable to defend against mutation. If they really need to be mutable (which is strange for autowired fields), make access thread-safe. Thread-safety can be achieved e.g. by proper synchronization and use the @GuardedBy annotation or use of volatile. Notes 1. Autowiring/injection is thread safe, yet make sure no other thread-unsafe assignment is made to that field. 2. In case you are sure the Component is used in single threaded context only (e.g. a Tasklet), annotate the class with @NotThreadSafe to make this explicit. 3. Use package-private and @VisibleForTesting for methods (e.g. setters) used for JUnit only. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="jpinpoint-rule,multi-threading" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassDeclaration[ ( pmd-java:hasAnnotation('org.springframework.stereotype.Component') or pmd-java:hasAnnotation('org.springframework.stereotype.Service') or pmd-java:hasAnnotation('org.springframework.stereotype.Controller') or pmd-java:hasAnnotation('org.springframework.stereotype.RestController') or pmd-java:hasAnnotation('org.springframework.stereotype.Repository') or (pmd-java:hasAnnotation('javax.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('javax.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('javax.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) or (pmd-java:hasAnnotation('jakarta.ejb.Singleton') and ModifierList/Annotation[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagement')]/AnnotationMemberList/MemberValuePair/FieldAccess[./TypeExpression[pmd-java:typeIs('jakarta.ejb.ConcurrencyManagementType')] and @Name="BEAN"]) ) (: not shared when request or prototype scope :) and not(pmd-java:hasAnnotation('org.springframework.context.annotation.Scope') and .//AnnotationMemberList/MemberValuePair[ StringLiteral[contains(@Image,'request') or contains(@Image,'prototype')] or FieldAccess[@Name='SCOPE_REQUEST' or @Name='SCOPE_PROTOTYPE']/TypeExpression[pmd-java:typeIs('org.springframework.beans.factory.config.ConfigurableBeanFactory')]] ) and not(pmd-java:hasAnnotation('org.springframework.context.annotation.RequestScope')) (: if @NotThreadSafe no checking :) and not(pmd-java:hasAnnotation('net.jcip.annotations.NotThreadSafe')) (: no checking if @ConfigurationProperties and no @Setter :) and not(pmd-java:hasAnnotation('org.springframework.boot.context.properties.ConfigurationProperties')) and not(pmd-java:hasAnnotation('lombok.Setter')) ] [not(pmd-java:modifiers()='static')] //FieldDeclaration[ not(pmd-java:modifiers()=('final','volatile')) and not( pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') or (pmd-java:hasAnnotation('javax.persistence.PersistenceContext') or pmd-java:hasAnnotation('jakarta.persistence.PersistenceContext')) or (pmd-java:hasAnnotation('javax.ejb.EJB') or pmd-java:hasAnnotation('jakarta.ejb.EJB')) or (pmd-java:hasAnnotation('javax.inject.Inject') or pmd-java:hasAnnotation('jakarta.inject.Inject')) or (pmd-java:hasAnnotation('javax.annotation.Resource') or pmd-java:hasAnnotation('jakarta.annotation.Resource')) or pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Value') or ModifierList/Annotation[@SimpleName='GuardedBy'] ) (: field is accessed in an Autowired annotated method :) and ./VariableDeclarator/VariableId/@Name = ancestor::ClassDeclaration//ConstructorDeclaration[ pmd-java:hasAnnotation('org.springframework.beans.factory.annotation.Autowired') ]//FieldAccess/@Name ] ]]></value> </property> </properties> <example><![CDATA[ @RestController public class DoodleController { private String name; // bad: autowired constructor, yet non-final @Autowired public DoodleController(String name) { this.name = name; } } ]]></example> </rule> <rule name="MinimizeActionModelMapInSession" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="ModelMap in action method is not cleared. This may bloat the session." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#tmsu12"> <description>A ModelMap is used in an action method typically for form validation and not cleared. Problem: the ModelMap is put in the session by Spring. This is typically a large object which may bloat the session. Solution: clear the ModelMap right after the validation in the happy flow. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,memory,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //MethodDeclaration[ pmd-java:modifiers()='public' and .//FormalParameter[pmd-java:typeIs('org.springframework.ui.ModelMap')] and ( .//FormalParameter[pmd-java:typeIs('javax.portlet.ActionRequest')] or pmd-java:hasAnnotation('org.springframework.web.portlet.bind.annotation.ActionMapping') ) and not(./Block//MethodCall[pmd-java:matchesSig('org.springframework.ui.ModelMap#clear()')]) ] ]]></value> </property> </properties> <example><![CDATA[ public static final String SAVE_ACTION = "action=saveDetails"; public static final String SUBMIT_OK = "submit_ok"; @ActionMapping(params = { SAVE_ACTION, SUBMIT_OK }) public void validateDetails(ActionRequest request, ModelMap modelMap) { validateWith(modelMap); modelMap.clear(); // good } ]]></example> </rule> <rule name="SynchronizeForKeyInCacheable" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Synchronize access for each key in @Cacheable" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#improper-caching"> <description>The cache by default allows multiple threads accessing by the same key. Problem: if the value of the key is not available from the cache, it will be fetched/computed by multiple threads while only one time is needed. Solution: Let only the first accessing thread fetch/compute the value and block others until the value is in the cache. Add attribute sync = "true" to achieve this. (Assuming the cache implementation supports it.) (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,jpinpoint-rule,multi-threading,performance,sustainability-low" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //Annotation[ pmd-java:typeIs('org.springframework.cache.annotation.Cacheable') and not(exists(AnnotationMemberList/MemberValuePair[@Name='sync'])) ] ]]></value> </property> </properties> <example> <![CDATA[ public class Bad { @Cacheable(value = "Cache1") // bad public String bad() { ... } } class Good { @Cacheable(value = "Cache1", sync = "true") // good public String good() { ... } } ]]> </example> </rule> <rule name="UseClearKeyGeneratorName" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Use a specific name for this KeyGenerator class which makes clear where to use it." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ic17"> <description>This class implementing Spring's KeyGenerator uses a generic name, CacheKeyGenerator or CacheKeyGeneration. Problem: It is unclear where this KeyGenerator should be used, for which cache and/or for which methods. If used on the wrong caches or methods, it may lead to cache key mix-up and user data mix-up. Solution: Make the name specific so that it is clear where to apply this KeyGenerator in @Cacheable. (jpinpoint-rules)</description> <priority>3</priority> <properties> <property name="tags" value="bad-practice,confusing,data-mix-up,jpinpoint-rule" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //ClassDeclaration[ pmd-java:typeIs('org.springframework.cache.interceptor.KeyGenerator') and not(pmd-java:modifiers()='abstract') and (@SimpleName="CacheKeyGenerator" or @SimpleName="CacheKeyGeneration") ] ]]></value> </property> </properties> <example> <![CDATA[ import org.springframework.cache.interceptor.KeyGenerator; public class CacheKeyGenerator implements KeyGenerator { // bad, unclear name public Object generate(Object target, Method method, Object... params) { // build key and return it } } ]]> </example> </rule> <rule name="UseExplicitKeyGeneratorForCacheable" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" message="Use a KeyGenerator to generate a correct and unique key per cached value, do not rely on the implicit default key generation." typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaCodePerformance.md#ic14"> <description> Problem: With default key generation, an object of Spring's SimpleKey class is used and its value is composed of just the method parameter(s). It does not include the method, which is unclear and risky. Solution: Create a KeyGenerator and make it generate a unique key for the cache per cached value, by use of SimpleKey composed of method object and the parameters. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="bad-practice,jpinpoint-rule,performance" type="String" description="classification"/> <property name="xpath"> <value><![CDATA[ //Annotation[ pmd-java:typeIs('org.springframework.cache.annotation.Cacheable') and not(exists(AnnotationMemberList/MemberValuePair[@Name='keyGenerator'])) ] ]]></value> </property> </properties> <example> <![CDATA[ import org.springframework.cache.annotation.Cacheable; class Foo { @Cacheable(cacheNames = {"DATA"}, sync = true, keyGenerator = "cacheKeyGenerator") // good, keyGenerator present public Object getDataGood(String id) { return fetchFromBackend(id); } @Cacheable(value="DATA", sync = true) // bad, keyGenerator missing public Object getDataBad(String id) { return fetchFromBackend(id); } } ]]> </example> </rule> <!-- END Included file 'spring.xml' --> <!-- BEGIN Included file 'sql.xml' --> <rule name="AvoidFetchingWholeList" message="Avoid fetching the whole list of entities for the query, while only one entity is used" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaDataAccessPerformance.md#ida-trr04"> <description> Problem: Unused rows are fetched and transported, and unused jdbc buffer is allocated. Solution: Use query.getSingleResult() in stead of query.getResultList(). (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,io,jpinpoint-rule,memory,performance,sustainability-high" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ (: if getResultList is used to fetch entities and entities(0) is returned :) //ReturnStatement//MethodCall[pmd-java:matchesSig('java.util.List#get(int)')][./ArgumentList/NumericLiteral[@ValueAsInt=0]]/VariableAccess[ @Name = ancestor::MethodDeclaration//VariableDeclarator[ MethodCall[pmd-java:matchesSig('javax.persistence.TypedQuery#getResultList()') or pmd-java:matchesSig('jakarta.persistence.TypedQuery#getResultList()')] ]/VariableId/@Name and not( (: not when also another elem of entities is used in the method :) @Name = ancestor::MethodDeclaration//MethodCall[pmd-java:matchesSig('java.util.List#get(int)')][./ArgumentList/NumericLiteral[@ValueAsInt!=0]]/VariableAccess/@Name or (: not when also the whole list of entities is returned :) @Name = ancestor::MethodDeclaration//ReturnStatement//(ConditionalExpression|.)/VariableAccess[not(parent::MethodCall)]/@Name ) ] ]]> </value> </property> </properties> <example> <![CDATA[ public SomeEntity findByQueryBad(TypedQuery<Object> query) { final List<SomeEntity> entities = query.getResultList(); return !entities.isEmpty() ? entities.get(0) : null; } public SomeEntity findByQueryGood(TypedQuery<Object> query) throws NoResultException { return query.getSingleResult(); } ]]> </example> </rule> <rule name="AvoidHugeQueryFetchSize" message="Avoid a huge query fetch size, it consumes much memory." class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaDataAccessPerformance.md#ida-trm03"> <description> Problem: if huge numbers of result rows are fetched these are all stored in memory and this may introduce long gc times and out of memory risk. Solution: Set fetch size to 100 maximally. Only set it higher than 100 yet still max 500, if you are sure there is only little data returned per row, for instance 3 rather short columns. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,memory,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //MethodCall[ pmd-java:matchesSig('java.sql.Statement#setFetchSize(int)') or pmd-java:matchesSig('org.hibernate.Query#setFetchSize(int)') (:hibernate <6 :) or pmd-java:matchesSig('org.hibernate.query.Query#setFetchSize(int)') (:hibernate 6 :) or pmd-java:matchesSig('org.apache.openjpa.persistence.jdbc.JDBCFetchPlan#setFetchBatchSize(int)') ]/ArgumentList[ NumericLiteral[@ValueAsInt > 500] or VariableAccess/@Name = (ancestor::ClassBody//FieldDeclaration|ancestor::MethodDeclaration)//VariableDeclarator[NumericLiteral[@ValueAsInt > 500]]/VariableId/@Name ] ]]> </value> </property> </properties> </rule> <rule name="AvoidMultipleRoundtripsForQuery" message="Avoid multiple round trips for the same query" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" language="java" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaDataAccessPerformance.md#ida-trr05"> <description> Problem: Time is taken by the unnecessary round trip(s). Unnecessary work is performed. Solution: Execute the query only once. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="cpu,io,jpinpoint-rule,performance,sustainability-medium" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //MethodCall[pmd-java:matchesSig('javax.persistence.Query#getSingleResult()') or pmd-java:matchesSig('jakarta.persistence.Query#getSingleResult()')][ ancestor::MethodDeclaration[ count(.//MethodCall[@MethodName='createQuery']) = 1 and count(.//MethodCall[@MethodName='getResultList' or @MethodName='getSingleResult']) > 1 ] ] ]]> </value> </property> </properties> </rule> <rule name="AvoidSqlInExpression" message="Avoid a SQL IN-Expression, it fails for > 1000 arguments and pollutes the query plan cache / statement cache" class="net.sourceforge.pmd.lang.rule.xpath.XPathRule" dfa="false" language="java" typeResolution="true" externalInfoUrl="https://github.com/jborgers/PMD-jPinpoint-rules/tree/pmd7/docs/JavaDataAccessPerformance.md#ida-ino01"> <description> Problem: The number of values for the IN-argument list is limited, in Oracle to 1000. An error occurs when exceeding this limit. Additionally, a large IN list takes much time to transport to the database and be parsed. Moreover, each number of IN values used in a query results in a separate cache entry in e.g. the Prepared Statement Cache of the application server and in the Hibernate Query Plan Cache, resulting in higher memory usage and/or low cache hit ratio. Solution: Rewrite the query by replacing the IN-argument list by a sub query using the criteria used to fetch the IN arguments. Or often even better performing, an inner join using these criteria (depending on indexes etc. - recommended to test to be sure.) This way, the select and update are combined into one, which will also save one roundtrip. (jpinpoint-rules)</description> <priority>2</priority> <properties> <property name="tags" value="jpinpoint-rule,performance" type="String" description="classification"/> <property name="xpath"> <value> <![CDATA[ //MethodCall[( @MethodName='in' and ( ./TypeExpression[pmd-java:typeIs('org.hibernate.criterion.Restrictions')] or ./VariableAccess[pmd-java:typeIs('javax.persistence.criteria.CriteriaBuilder') or pmd-java:typeIs('jakarta.persistence.criteria.CriteriaBuilder')] ) ) or pmd-java:matchesSig('javax.persistence.criteria.Expression#in(_*)') or pmd-java:matchesSig('jakarta.persistence.criteria.Expression#in(_*)') or pmd-java:matchesSig('org.apache.openjpa.persistence.query.Expression#in(_*)') or pmd-java:matchesSig('org.hibernate.CriteriaBuilder#in(_*)') or pmd-java:matchesSig('org.hibernate.criterion.Restrictions#in(_*)') ][ (: in with a subquery is a solution :) not(ancestor::ArgumentList//MethodCall[@MethodName='value']/ArgumentList/VariableAccess[ pmd-java:typeIs('javax.persistence.criteria.Subquery') or pmd-java:typeIs('jakarta.persistence.criteria.Subquery') ]) ] | //ClassDeclaration[pmd-java:hasAnnotation('javax.persistence.NamedQueries') or pmd-java:hasAnnotation('jakarta.persistence.NamedQueries')] //Annotation[@SimpleName='NamedQueries']//StringLiteral[matches(@ConstValue, '\s+(IN|in)\s*\(?\s?:\w+')] | //StringLiteral[matches(@ConstValue, '\s+(IN|in)\s*\(?\s?:\w+')][ replace(@ConstValue, '^.+\s+(IN|in)\s*\(?\s?:(\w+).*$', '$2') = ancestor::MethodDeclaration//MethodCall[ pmd-java:matchesSig('javax.persistence.Query#setParameter(_*)') or pmd-java:matchesSig('jakarta.persistence.Query#setParameter(_*)') (: Arrays.asList is typically used with just a few parameter values, so no problem :) ]/ArgumentList[not(./MethodCall[pmd-java:matchesSig('java.util.Arrays#asList(_*)')])]/StringLiteral/@ConstValue ] ]]> </value> </property> </properties> <example> <![CDATA[ private void bad(CriteriaQuery cq, Root<Customer> cust) { cq.select(cust).where(cust.get("postalCode").in(codes)); // bad } private void good(CriteriaBuilder builder, CriteriaQuery cq, Root<Customer> cust, SubQuery<PostalCode> subquery) { cq.select(cust).where(builder.in(cust.get("postalCode")).value(subquery)); } ]]> </example> </rule> <!-- END Included file 'sql.xml' --> </ruleset>