/* * 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.concurrent.ThreadFactory; import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; import com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils; import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters; import net.sourceforge.pmd.lang.java.ast.ASTImplementsList; import net.sourceforge.pmd.lang.java.ast.ASTInitializer; import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTResultType; import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; import net.sourceforge.pmd.lang.java.ast.Token; /** * [Mandatory] Threads should be provided by thread pools. Explicitly creating threads is not allowed. * Note: Using thread pool can reduce the time of creating and destroying thread and save system resource. * If we do not use thread pools, lots of similar threads will be created which lead to * "running out of memory" or over-switching problems. * * Detection rule * New Thread can only be created in ThreadFactory.newThread method,as Runtime.getRuntime().addShutdownHook() parameter, * or in static block * * @author caikang * @date 2016/11/15 * @see ThreadShouldSetNameRule */ public class AvoidManuallyCreateThreadRule extends AbstractAliRule { private static final String METHOD_NEW_THREAD = "newThread"; @Override public Object visit(ASTAllocationExpression node, Object data) { if (node.getType() != Thread.class) { return super.visit(node, data); } if (isAddShutdownHook(node) || isInStaticInitializer(node)) { return super.visit(node, data); } //Allocation in lambda block is ignored if (node.getFirstParentOfType(ASTLambdaExpression.class) != null) { return super.visit(node, data); } ASTFieldDeclaration fieldDeclaration = node.getFirstParentOfType(ASTFieldDeclaration.class); //field declaration with thread allocated if (fieldDeclaration != null && NodeUtils.getNodeType(fieldDeclaration) == Thread.class) { return addViolationAndReturn(node, data); } //Declare thread factory field use lambda if (node.getDataFlowNode() == null && node.getFirstParentOfType(ASTLambdaExpression.class) != null) { if (fieldDeclaration == null || NodeUtils.getNodeType(fieldDeclaration) != ThreadFactory.class) { return addViolationAndReturn(node, data); } return super.visit(node, data); } //in newThread(Runnable) method is ok if (isInNewThreadMethod(node)) { return super.visit(node, data); } //implements of ThreadFactory boolean isThreadFactory = (checkForNamingClass(node) || threadFactoryVariable(node)) && isInPrimaryOrProtectedMethod(node); if (isThreadFactory) { return super.visit(node, data); } return addViolationAndReturn(node, data); } private boolean isAddShutdownHook(ASTAllocationExpression node) { ASTBlockStatement blockStatement = node.getFirstParentOfType(ASTBlockStatement.class); if (blockStatement == null) { return false; } Token token = (Token)blockStatement.jjtGetFirstToken(); return Runtime.class.getSimpleName().equals(token.image); } private boolean isInStaticInitializer(ASTAllocationExpression node) { ASTInitializer initializer = node.getFirstParentOfType(ASTInitializer.class); return initializer != null && initializer.isStatic(); } private boolean threadFactoryVariable(ASTAllocationExpression node) { ASTMethodDeclaration methodDeclaration = node.getFirstParentOfType(ASTMethodDeclaration.class); if (methodDeclaration == null) { return false; } ASTVariableDeclarator variableDeclarator = methodDeclaration.getFirstParentOfType(ASTVariableDeclarator.class); return variableDeclarator != null && variableDeclarator.getType() == ThreadFactory.class; } private boolean isInNewThreadMethod(ASTAllocationExpression node) { ASTMethodDeclaration methodDeclaration = node.getFirstParentOfType(ASTMethodDeclaration.class); if (methodDeclaration == null) { return false; } if (!returnThread(methodDeclaration)) { return false; } if (!METHOD_NEW_THREAD.equals(methodDeclaration.getMethodName())) { return false; } List parameters = methodDeclaration.getFirstDescendantOfType(ASTFormalParameters.class) .findChildrenOfType(ASTFormalParameter.class); return parameters.size() == 1 && parameters.get(0).getFirstChildOfType(ASTType.class).getType() == Runnable.class; } private boolean isInPrimaryOrProtectedMethod(ASTAllocationExpression node) { ASTMethodDeclaration methodDeclaration = node.getFirstParentOfType(ASTMethodDeclaration.class); return methodDeclaration != null && returnThread(methodDeclaration) && (methodDeclaration.isPrivate() || methodDeclaration.isProtected()); } private boolean returnThread(ASTMethodDeclaration methodDeclaration) { ASTResultType resultType = methodDeclaration.getFirstChildOfType(ASTResultType.class); ASTType type = resultType.getFirstChildOfType(ASTType.class); return type != null && type.getType() == Thread.class; } private Object addViolationAndReturn(ASTAllocationExpression node, Object data) { addViolationWithMessage(data, node, "java.concurrent.AvoidManuallyCreateThreadRule.violation.msg"); return super.visit(node, data); } private boolean checkForNamingClass(ASTAllocationExpression node) { ASTClassOrInterfaceDeclaration classOrInterfaceDeclaration = node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); if (classOrInterfaceDeclaration == null) { return false; } ASTImplementsList implementsList = classOrInterfaceDeclaration.getFirstChildOfType(ASTImplementsList.class); if (implementsList == null) { return false; } List interfaceTypes = implementsList.findChildrenOfType(ASTClassOrInterfaceType.class); for (ASTClassOrInterfaceType type : interfaceTypes) { if (type.getType() == ThreadFactory.class) { return true; } } return false; } }