com.alibaba.p3c.pmd.lang.java.rule.concurrent.ThreadPoolCreationRule 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.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.lang.java.ast.Token;
/**
* [Mandatory] A thread pool should be created by ThreadPoolExecutor rather than Executors.
* These would make the parameters of the thread pool understandable.
* It would also reduce the risk of running out of system resource.
*
* @author caikang
* @date 2016/11/14
*/
public class ThreadPoolCreationRule extends AbstractAliRule {
private static final String DOT = ".";
private static final String COLON = ";";
private static final String NEW = "new";
private static final String EXECUTORS_NEW = Executors.class.getSimpleName() + DOT + NEW;
private static final String FULL_EXECUTORS_NEW = Executors.class.getName() + DOT + NEW;
private static final String BRACKETS = "()";
private static final String NEW_SCHEDULED = "newScheduledThreadPool";
private static final String NEW_SINGLE_SCHEDULED = "newSingleThreadScheduledExecutor";
@Override
public Object visit(ASTCompilationUnit node, Object data) {
Object superResult = super.visit(node, data);
Info info = new Info();
List importDeclarations = node.findChildrenOfType(ASTImportDeclaration.class);
for (ASTImportDeclaration importDeclaration : importDeclarations) {
ASTName name = importDeclaration.getFirstChildOfType(ASTName.class);
info.executorsUsed = info.executorsUsed
|| (name.getType() == Executors.class || Executors.class.getName().equals(name.getImage()));
if (name.getImage().startsWith(Executors.class.getName() + DOT)) {
info.importedExecutorsMethods.add(name.getImage());
}
}
List primaryExpressions = node.findDescendantsOfType(ASTPrimaryExpression.class);
for(ASTPrimaryExpression primaryExpression : primaryExpressions){
if (!info.executorsUsed && info.importedExecutorsMethods.isEmpty()) {
continue;
}
Token initToken = (Token)primaryExpression.jjtGetFirstToken();
if (!checkInitStatement(initToken, info)) {
addViolationWithMessage(data, primaryExpression,"java.concurrent.ThreadPoolCreationRule.violation.msg");
}
}
return superResult;
}
private boolean checkInitStatement(Token token, Info info) {
String fullAssignStatement = getFullAssignStatement(token);
// do not check newScheduledThreadPool and newSingleThreadScheduledExecutor
if (NEW_SCHEDULED.equals(fullAssignStatement) || NEW_SINGLE_SCHEDULED.equals(fullAssignStatement)) {
return true;
}
if (fullAssignStatement.startsWith(EXECUTORS_NEW)) {
return false;
}
if (!fullAssignStatement.startsWith(NEW) && !fullAssignStatement.startsWith(FULL_EXECUTORS_NEW)) {
return true;
}
// code with lambda
int index = fullAssignStatement.indexOf(BRACKETS);
if (index == -1) {
return true;
}
fullAssignStatement = fullAssignStatement.substring(0, index);
// java.util.concurrent.Executors.newxxxx
if (info.importedExecutorsMethods.contains(fullAssignStatement)) {
return false;
}
// static import
return !info.importedExecutorsMethods.contains(Executors.class.getName() + DOT + fullAssignStatement);
}
private String getFullAssignStatement(final Token token) {
if (token == null) {
return "";
}
StringBuilder sb = new StringBuilder(48);
Token next = token;
while (next.next != null && !COLON.equals(next.image)) {
sb.append(next.image);
next = next.next;
}
return sb.toString();
}
class Info {
boolean executorsUsed;
Set importedExecutorsMethods = new HashSet<>();
}
}