jPinpoint specific rules for performance aware Java coding, sponsored by Rabobank.(jpinpoint-rules)
Problem: A proxy object is created by CDI for explicit references, they are not de-referenced implicitly and become a memory leak.
Solution: Destroy the reference explicitly.
(jpinpoint-rules)
2
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)
3
0
]
]]>
Problem: java.text.DecimalFormat and java.text.ChoiceFormat are thread-unsafe. The usual solution
is to create a new local one when needed in a method.
(jpinpoint-rules)
2
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: Use org.apache.http.impl.conn.PoolingHttpClientConnectionManager and org.apache.http.impl.client.HttpClientBuilder. (jpinpoint-rules)
3
Problem: Potential bug: expected are different assignments in different cases.
Solution: assign different values in different cases, common assignments should be taken out of the switch.
(jpinpoint-rules)
0
or preceding-sibling::SwitchLabel[@Default='true'])
]
]]>
2
A regular expression is compiled implicitely 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)
2
5 and
(matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+'))] or Name
and not(../PrimarySuffix)
and not (
Name/@Image=ancestor::MethodDeclaration//VariableDeclaratorId/@Image)
and not (
Name[@Image=ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration/VariableDeclarator[
VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[string-length(@Image) < 6 or not
(matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+'))]
]/VariableDeclaratorId/@Image])
]
,
//MethodDeclaration//PrimaryPrefix/Name[ends-with(@Image, '.split') or ends-with(@Image, 'getPathMatcher')]/../../PrimarySuffix/Arguments[@ArgumentCount=1]//Expression[1]//PrimaryPrefix[
Literal[string-length(@Image) > 5 and
matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+')] or Name
and not(../PrimarySuffix)
and not (
Name/@Image=ancestor::MethodDeclaration//VariableDeclaratorId/@Image)
and not (
Name[@Image=ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration/VariableDeclarator[
VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[string-length(@Image) < 6 or not
(matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+'))]
]/VariableDeclaratorId/@Image])
]
,
//MethodDeclaration//PrimarySuffix[@Image='getPathMatcher']/../PrimarySuffix/Arguments[@ArgumentCount=1]/ArgumentList/Expression[1]/PrimaryExpression/PrimaryPrefix[
Literal[string-length(@Image) > 5] or Name
and not(../PrimarySuffix)
and not (
Name/@Image=ancestor::MethodDeclaration//VariableDeclaratorId/@Image)
and not (
Name[@Image=ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration/VariableDeclarator[
VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[string-length(@Image) < 6]]/VariableDeclaratorId/@Image])
]
,
(: --- String.matches called on formalparams, locals and fields --- :)
//MethodDeclaration//PrimaryPrefix/Name[ends-with(@Image, '.matches')]
[
(exists(index-of((ancestor::MethodDeclaration//FormalParameter[pmd-java:typeIs('java.lang.String')]/VariableDeclaratorId/@Image), substring-before(@Image,'.')))
or
exists(index-of((ancestor::MethodDeclaration//LocalVariableDeclaration/Type[pmd-java:typeIs('java.lang.String')]/../VariableDeclarator/VariableDeclaratorId/@Image), substring-before(@Image,'.')))
or
exists(index-of((ancestor::ClassOrInterfaceBody//FieldDeclaration[pmd-java:typeIs('java.lang.String')]/VariableDeclarator/VariableDeclaratorId/@Image), substring-before(@Image,'.'))))
and
(: for matches param is >5 literal or something named :)
../../PrimarySuffix/Arguments[@ArgumentCount=1]//Expression[1]//PrimaryPrefix[
Literal[string-length(@Image) > 5] or Name
(: exclude method calls :)
and not(../PrimarySuffix)
(: exclude for param is method arg or local :)
and not (
Name/@Image=ancestor::MethodDeclaration//VariableDeclaratorId/@Image)
(: exclude for param is short fields :)
and not (
Name[@Image=ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration/VariableDeclarator[
VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[string-length(@Image) < 6]
]/VariableDeclaratorId/@Image])
]])
]]>
Default constructor of ByteArrayOutputStream or StringWriter is used. Problem: It allocates a small buffer as capacity which usually needs several expensive expansions.
Solution: Presize the ByteArrayOutputStream or StringWriter with an initial capacity such that an expansion is not needed in most cases.
(jpinpoint-rules)
2
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 StringBulder.append.
(jpinpoint-rules)
2
1]/BlockStatement[position()=last()]
|
//MethodDeclaration/Block[
count(
./BlockStatement/Statement//StatementExpression[
./PrimaryExpression/PrimaryPrefix/Name[
@Image = ../../../Expression/AdditiveExpression/PrimaryExpression/PrimaryPrefix/Name/@Image
and
@Image = ./../../../../../../../..//VariableDeclaratorId/../../Type/ReferenceType/ClassOrInterfaceType[typeIs('java.lang.String')]/../../../VariableDeclarator/VariableDeclaratorId/attribute::Image
]
]) > 1 ]//BlockStatement[position()=last()]//StatementExpression/Expression/AdditiveExpression[@Image = '+']
]]>
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)
2
XPathExpression is created and compiled on every method call. Problem: Creation XPath and compilation of XPathExpression takes time. It may slow down your application.
Solution: 1. Avoid XPath usage. 2. Since XPath and XPathExpression classes are thread-unsafe, they are not easily cached. Caching in Thread locals may be a solution.
(jpinpoint-rules)
2
Problem: Recreating a DateTimeFormatter is relatively expensive.
Solution: org.joda.time.format.DateTimeFormatter or 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)
2
Problem: Reflection is relatively expensive.
Solution: Avoid to use reflection. Use the non-reflective, explicit way, preferably using Guava.
(jpinpoint-rules)
2
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 a Joda-Time DateTimeFormat to create a specific DateTimeFormatter or Java 8 java.time.DateTimeFormatter. These classes are immutable, thus thread-safe and can be made static.
(jpinpoint-rules)
2
Problem: StringBuffer introduces locking overhead because it is thread safe. Its thread-safety is rarely needed.
Solution: Replace StringBuffer by StringBuilder. (jpinpoint-rules)
3
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)
2
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)
2
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 joda-time or Java 8 java.time.
(jpinpoint-rules)
2
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)
2
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)
3
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)
2
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. 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... and @Entity objects are ignored because they are assumed to be not used as value objects.
(jpinpoint-rules)
3 or starts-with(@Image, 'is') and string-length(@Image) > 2]
[../ResultType/Type/ReferenceType/ClassOrInterfaceType/@Image =
ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Static='false']/Type/ReferenceType/ClassOrInterfaceType/@Image]
)
and
(not (
ancestor::ClassOrInterfaceBody//MethodDeclaration[@Public='true' and @Static='false']/MethodDeclarator[@Image='equals' or @Image='hashCode'])
)
and
((ancestor::ClassOrInterfaceBody//MethodDeclaration[@Public='true' and @Static='false']/MethodDeclarator/@Image='toString'
and
count(ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Static='false']) <=
(1 + count(ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration
[@Public='true' and @Static='false']/MethodDeclarator[starts-with(@Image, 'get') and string-length(@Image) > 3 or starts-with(@Image, 'is') and string-length(@Image) > 2]))
)
or
ancestor::ClassOrInterfaceDeclaration[ends-with(@Image, 'Dto')]
or
count(ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Static='false']) =
count(ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration
[@Public='true' and @Static='false']/MethodDeclarator[starts-with(@Image, 'get') and string-length(@Image) > 3 or starts-with(@Image, 'is') and string-length(@Image) > 2])
)]
]
]]>
3
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)
2
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)
2
An attribute is set in the session and not removed. Problem: This may be a large object and data in the sessions takes heap space and stay in the session until time-out. This may take substantial heap space.
Solution: remove the attribute if not really needed in the session, remove it from the session as soon as possible. Alternatively, use render parameters.
(jpinpoint-rules)
2
Problem: Jackson ObjectMapper creation is expensive because it does much class loading.
Solution: Since ObjectMapper objects are thread-safe after configuration in one thread, they can be shared afterwards between requests and reused. So, reuse created instances, from a static field.
(jpinpoint-rules)
2
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)
2
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.get(arg).
Solution: Execute the operation only conditionally and utilize SLF4J formatting with {}-placeholders. (jpinpoint-rules)
2
(Informative) Problem: This rule detects problems, suppressing them without full knowledge can lead to the problems this rule is trying to prevent.
Solution: Suppress warnings judiciously based on full knowledge and report reasons to suppress (false positives) to the rule maintainers so these can be fixed. (jpinpoint-rules)
4
(Informative) Problem: This rule detects high risk problems, suppressing them without full knowledge can lead to incidents like customer data mix-up, corrupt data, server crashes or very bad performance.
Solution: Suppress warnings judiciously based on full knowledge and report reasons to suppress (false positives) to the rule maintainers so these can be fixed. (jpinpoint-rules)
4
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)
2
Problem: A Calendar is a heavyweight object and expensive to create.
Solution: Use 'new Date()', Java 8+ java.time.[Local/Zoned]DateTime.now() or joda time '[Local]DateTime.now()'.
(jpinpoint-rules)
2
2 and ../PrimarySuffix[last()-1][@Image = 'getTime' or @Image='getTimeInMillis']]
|
//Block/BlockStatement//Expression/PrimaryExpression/
PrimaryPrefix/Name[typeIs('java.util.Calendar') and (ends-with(@Image,'.getTime') or ends-with(@Image,'.getTimeInMillis'))]
|
//ClassOrInterfaceType[typeIs('org.joda.time.DateTime') or typeIs('org.joda.time.LocalDateTime')][../Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name[ends-with(@Image, 'Calendar.getInstance')]]
]]>
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)
2
0)
and not(PrimaryExpression/PrimaryPrefix/Name/@Image=
ancestor::ClassOrInterfaceBody//FieldDeclaration[@Final='true']//VariableDeclaratorId/@Image)
and not(PrimaryExpression/PrimaryPrefix/Name/@Image=
ancestor::Block//LocalVariableDeclaration[@Final='true']//VariableDeclaratorId/@Image)
]]
]]>
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)
2
values = Arrays.asList("tic ", "tac ", "toe ");
for (String val : values) {
log += val;
}
return log;
}
private String good(String arg) {
StringBuilder sb = new StringBuilder();
List values = Arrays.asList("tic ", "tac ", "toe ");
for (String val : values) {
sb.append(val);
}
return sb.toString();
}
}
]]>
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)
2
Problem: Stalls indefinitely in case of hanging threads and consumes a thread.
Solution: Provide a timeout value and handle the timeout.
(jpinpoint-rules)
2
complFuture) throws Exception {
return complFuture.get(); // bad
}
public static String good(CompletableFuture complFuture) throws Exception {
return complFuture.get(10, TimeUnit.SECONDS); // good
}
]]>
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 or use volatile. Consider lock contention.
(jpinpoint-rules)
2
0]))
and not (ancestor::ClassOrInterfaceBodyDeclaration/Annotation//Name[@Image='GuardedBy'])
])
,
(: static field, non-guarded, some often used known collection/array types, allocation side:)
(//ClassOrInterfaceDeclaration/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Static=true() and not (../Annotation//Name[@Image='GuardedBy'])]/
VariableDeclarator/VariableInitializer[((ArrayInitializer and count(ArrayInitializer/VariableInitializer) > 0)
or Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression[ArrayDimsAndInits and xs:int(ArrayDimsAndInits and (xs:int(ArrayDimsAndInits/Expression//Literal/@Image) > 0 or ArrayDimsAndInits/Expression//Name))]
or Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression[(pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet'))]
or Expression/PrimaryExpression/PrimaryPrefix/Name[@Image='Arrays.asList']
)])
,
(: static-block allocations of non-empty arrays :)
//Initializer//AllocationExpression[((ArrayDimsAndInits and ((xs:int(ArrayDimsAndInits/Expression//Literal/@Image) > 0) or exists(ArrayDimsAndInits/Expression//Name) or exists(ArrayDimsAndInits/ArrayInitializer//Expression)))
or
(: static-block allocations of known mutable types :)
ClassOrInterfaceType[pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet')])
and
(: given the field is not @GuardedBy :)
ancestor::StatementExpression/PrimaryExpression/PrimaryPrefix/Name/@Image = ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration[count(Annotation//Name[@Image='GuardedBy']) = 0]/FieldDeclaration//VariableDeclaratorId/@Image
]
]]>
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)
1
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)
2
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. 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. 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)
2
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. Autowiring/injection is thread safe, yet 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)
3
0])])
(: or in-line allocation of known mutable collection types :)
or (VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression/ClassOrInterfaceType[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/VariableDeclaratorId/@Image = ancestor::ClassOrInterfaceBody//ConstructorDeclaration//StatementExpression/Expression[pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet')]/../..//Name/@Image)
(: mutable types not annotated with GuardedBy :)
) and not (../Annotation//Name[@Image='GuardedBy'])
]
]]>
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. 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. Autowiring/injection is thread safe, yet 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)
2
0])])
(: or in-line allocation of known mutable collection types :)
or (VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression[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/VariableDeclaratorId/@Image = ancestor::ClassOrInterfaceBody//ConstructorDeclaration//StatementExpression/Expression[pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet')]/../..//Name/@Image)
(: not annotated GuardedBy :)
)
and not (../Annotation//Name[@Image='GuardedBy'])
]
]]>
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)
3
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)
2
Improper combination of annotations. Problem: these annotations are not meant to be combined and may cause unexpected and unwanted behavior.
Solution: remove the inappropriate annotation.
Don't combine 2+ of [@Component, @Service, @Configuration, @Controller, @Repository, @Entity] (Spring/JPA)
Don't combine [@Data with @Value] and [@Data or @Value] with any of [@ToString, @EqualsHashCode, @Getter, @Setter, @RequiredArgsConstructor] (Lombok)
(jpinpoint-rules)
2
1]
|
//ClassOrInterfaceBodyDeclaration[count(./Annotation/MarkerAnnotation/Name[@Image='Component' or @Image='Service' or @Image='Configuration' or @Image='Controller' or @Image='Repository' or @Image='Entity']) > 1]
|
//TypeDeclaration[count(./Annotation/MarkerAnnotation/Name[@Image='Data' or @Image='Value']) > 1]
|
//ClassOrInterfaceBodyDeclaration[count(./Annotation/MarkerAnnotation/Name[@Image='Data' or @Image='Value']) > 1]
|
//TypeDeclaration[./Annotation/MarkerAnnotation/Name[@Image='Data' or @Image='Value'] and ./Annotation/MarkerAnnotation/Name[@Image='ToString' or @Image='EqualsAndHashCode' or @Image='Getter' or @Image='Setter' or @Image='RequiredArgsConstructor']]
|
//ClassOrInterfaceBodyDeclaration[./Annotation/MarkerAnnotation/Name[@Image='Data' or @Image='Value'] and ./Annotation/MarkerAnnotation/Name[@Image='ToString' or @Image='EqualsAndHashCode' or @Image='Getter' or @Image='Setter' or @Image='RequiredArgsConstructor']]
]]>
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)
2
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)
2
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 icw a redirectUrl like
redirect:/redirectUrl?someAttribute={someAttribute}.(jpinpoint-rules)
2
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)
4
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)
2
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, like 3 rather short columns.
(jpinpoint-rules)
500]]]
|
//MethodDeclaration//PrimaryExpression[PrimaryPrefix/Name[ends-with(@Image, '.setFetchSize')]
[ancestor::PrimaryExpression/PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name[@Image =
ancestor::ClassOrInterfaceBody//VariableDeclarator/VariableDeclaratorId/@Image
[ancestor::VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[@Image > 500]]]]]
]]>
2
Problem: Time is taken by the unnecessary roundtrip(s). Unnecessary work is performed.
Solution: Execute the query only once.
(jpinpoint-rules)
1]]
]]>
2
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)
2