Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package checks;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;

class ScheduledThreadPoolExecutorMaximumPoolSizeCheckSample {

void directCall() {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
executor.setMaximumPoolSize(10); // Noncompliant {{Remove this "setMaximumPoolSize" call; it has no effect on ScheduledThreadPoolExecutor.}}
// ^^^^^^^^^^^^^^^^^^
}

void afterOtherConfig() {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
executor.setCorePoolSize(8);
executor.setMaximumPoolSize(10); // Noncompliant
// ^^^^^^^^^^^^^^^^^^
}

void variableArgument() {
int maxSize = 20;
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
executor.setMaximumPoolSize(maxSize); // Noncompliant
// ^^^^^^^^^^^^^^^^^^
}

ScheduledThreadPoolExecutor createPool() {
return new ScheduledThreadPoolExecutor(5);
}

void methodReturnedInstance() {
createPool().setMaximumPoolSize(10); // Noncompliant
// ^^^^^^^^^^^^^^^^^^
}

void onSubtype() {
CustomScheduledExecutor custom = new CustomScheduledExecutor(5);
custom.setMaximumPoolSize(10); // Noncompliant
// ^^^^^^^^^^^^^^^^^^
}

// Compliant cases

void setCorePoolSizeOnScheduled() {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
executor.setCorePoolSize(10); // Compliant - correct way to control pool size
}

void setMaximumPoolSizeOnThreadPoolExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L,
java.util.concurrent.TimeUnit.SECONDS,
new java.util.concurrent.LinkedBlockingQueue<>());
executor.setMaximumPoolSize(20); // Compliant - has effect on ThreadPoolExecutor
}

void otherConfigOnScheduled() {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
executor.setKeepAliveTime(60L, java.util.concurrent.TimeUnit.SECONDS); // Compliant
executor.allowCoreThreadTimeOut(true); // Compliant
}

static class CustomScheduledExecutor extends ScheduledThreadPoolExecutor {
CustomScheduledExecutor(int corePoolSize) {
super(corePoolSize);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SonarQube Java
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* You can redistribute and/or modify this program under the terms of
* the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.java.checks;

import org.sonar.check.Rule;
import org.sonar.java.checks.methods.AbstractMethodDetection;
import org.sonar.java.model.ExpressionUtils;
import org.sonar.plugins.java.api.semantic.MethodMatchers;
import org.sonar.plugins.java.api.tree.MethodInvocationTree;

@Rule(key = "S9132")
public class ScheduledThreadPoolExecutorMaximumPoolSizeCheck extends AbstractMethodDetection {

@Override
protected MethodMatchers getMethodInvocationMatchers() {
return MethodMatchers.create()
.ofSubTypes("java.util.concurrent.ScheduledThreadPoolExecutor")
.names("setMaximumPoolSize")
.addParametersMatcher("int")
.build();
}

@Override
protected void onMethodInvocationFound(MethodInvocationTree mit) {
reportIssue(ExpressionUtils.methodName(mit),
"Remove this \"setMaximumPoolSize\" call; it has no effect on ScheduledThreadPoolExecutor.");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* SonarQube Java
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* You can redistribute and/or modify this program under the terms of
* the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.java.checks;

import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.CheckVerifier;

import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath;

class ScheduledThreadPoolExecutorMaximumPoolSizeCheckTest {
@Test
void test() {
CheckVerifier.newVerifier()
.onFile(mainCodeSourcesPath("checks/ScheduledThreadPoolExecutorMaximumPoolSizeCheckSample.java"))
.withCheck(new ScheduledThreadPoolExecutorMaximumPoolSizeCheck())
.verifyIssues();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<h2>Why is this an issue?</h2>
<p>Scheduled thread pool executors are designed with a fixed-size thread pool architecture. Unlike standard thread pool implementations, they use an
unbounded work queue and maintain a fixed number of threads equal to the core pool size configuration.</p>
<p>When you call <code>setMaximumPoolSize()</code> on a <code>ScheduledThreadPoolExecutor</code>, the call completes without error, but it has no effect
on the executor's behavior. The pool will never create threads beyond the core pool size, regardless of how high you set the maximum pool size.</p>
<p>This happens because scheduled thread pool executors override the task queuing behavior. All tasks go into an unbounded queue, and the executor
never needs to create additional threads beyond the core pool size to handle the workload.</p>
<p>Attempting to adjust the maximum pool size suggests a misunderstanding of how this executor works, and indicates that the code may not behave as
intended.</p>
<h2>How to fix it</h2>
<p>Remove the call to <code>setMaximumPoolSize()</code> and use <code>setCorePoolSize()</code> instead to control the number of threads in the pool.</p>
<h3>Noncompliant code example</h3>
<pre>
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
executor.setMaximumPoolSize(10); // Noncompliant
</pre>
<h3>Compliant solution</h3>
<pre>
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
executor.setCorePoolSize(10); // Pool now maintains exactly 10 threads
</pre>
<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ScheduledThreadPoolExecutor.html">Oracle Java SE -
ScheduledThreadPoolExecutor (Java SE 17)</a></li>
</ul>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"title": "\"ScheduledThreadPoolExecutor.setMaximumPoolSize\" should not be called",
"type": "CODE_SMELL",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-9132",
"sqKey": "S9132",
"scope": "All",
"quickfix": "unknown"
}
Loading