com.alibaba.p3c.pmd.lang.java.rule.concurrent.AvoidConcurrentCompetitionRandomRule Maven / Gradle / Ivy
/*
* Copyright 1999-2017 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.p3c.pmd.lang.java.rule.concurrent;
import java.util.List;
import java.util.Random;
import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule;
import com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils;
import com.alibaba.p3c.pmd.lang.java.util.VariableUtils;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
import net.sourceforge.pmd.lang.java.ast.ASTExtendsList;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
import org.jaxen.JaxenException;
/**
* 6.13 [Recommended] Avoid using Random instance by multiple threads.
* Although it is safe to share this instance, competition on the same seed will damage performance.
* Note: Random instance includes instances of java.util.Random and Math.random().
*
* @author caikang
* @date 2017/03/29
*/
public class AvoidConcurrentCompetitionRandomRule extends AbstractAliRule {
private static final String XPATH_TPL = "//StatementExpression/PrimaryExpression"
+ "/PrimaryPrefix/Name[starts-with(@Image,'%s.')]";
private static final String MATH_RANDOM_METHOD = ".random";
private static final String MESSAGE_KEY_PREFIX = "java.concurrent.AvoidConcurrentCompetitionRandomRule";
@Override
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
ASTExtendsList extendsList = node.getFirstChildOfType(ASTExtendsList.class);
if (extendsList == null) {
return super.visit(node, data);
}
if (!hasThread(extendsList)) {
return super.visit(node, data);
}
List methodDeclarations = node.findDescendantsOfType(ASTMethodDeclaration.class);
if (methodDeclarations == null || methodDeclarations.isEmpty()) {
return super.visit(node, data);
}
checkMathRandom(methodDeclarations, data);
List fieldDeclarations = node.findDescendantsOfType(ASTFieldDeclaration.class);
if (fieldDeclarations == null || fieldDeclarations.isEmpty()) {
return super.visit(node, data);
}
for (ASTFieldDeclaration fieldDeclaration : fieldDeclarations) {
if (NodeUtils.getNodeType(fieldDeclaration) == Random.class
&& fieldDeclaration.isStatic()) {
checkRandom(fieldDeclaration, methodDeclarations, data);
}
}
return super.visit(node, data);
}
private void checkMathRandom(List methodDeclarations, Object data) {
for (ASTMethodDeclaration methodDeclaration : methodDeclarations) {
List primaryPrefixes
= methodDeclaration.findDescendantsOfType(ASTPrimaryPrefix.class);
if (primaryPrefixes == null || primaryPrefixes.isEmpty()) {
continue;
}
for (ASTPrimaryPrefix primaryPrefix : primaryPrefixes) {
if (primaryPrefix.getType() != Math.class) {
continue;
}
ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class);
if (name == null || name.getImage() == null || !name.getImage().endsWith(MATH_RANDOM_METHOD)) {
continue;
}
addViolationWithMessage(data, primaryPrefix,
MESSAGE_KEY_PREFIX + ".violation.msg.math.random");
}
}
}
private void checkRandom(ASTFieldDeclaration fieldDeclaration, List methodDeclarations,
Object data) {
for (ASTMethodDeclaration methodDeclaration : methodDeclarations) {
try {
List nodes = methodDeclaration.findChildNodesWithXPath(String.format(XPATH_TPL,
VariableUtils.getVariableName(fieldDeclaration)));
if (nodes == null || nodes.isEmpty()) {
continue;
}
for (Node rvNode : nodes) {
addViolationWithMessage(data, rvNode,
MESSAGE_KEY_PREFIX + ".violation.msg.random",
new Object[] {rvNode.getImage()});
}
} catch (JaxenException ignore) {
}
}
}
private boolean hasThread(ASTExtendsList extendsList) {
List typeList = extendsList.findChildrenOfType(ASTClassOrInterfaceType.class);
if (typeList == null || typeList.isEmpty()) {
return false;
}
for (ASTClassOrInterfaceType type : typeList) {
if (type.getType() == Thread.class) {
return true;
}
}
return false;
}
}