Skip to content

Commit aae0b59

Browse files
committed
1 parent a267f94 commit aae0b59

File tree

37 files changed

+961
-430
lines changed

37 files changed

+961
-430
lines changed

checkstyle/checkstyle.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,39 @@
194194

195195
<!-- Usage of Ignite abbrevations check. -->
196196
<module name="org.apache.ignite.tools.checkstyle.IgniteAbbrevationsRule"/>
197+
198+
<module name="org.apache.ignite.tools.checkstyle.ClassUsageRestrictionRule">
199+
<property name="className" value="java.util.concurrent.ForkJoinPool"/>
200+
<property name="factoryMethods" value="commonPool"/>
201+
<property name="substitutionClassName" value="org.apache.ignite.internal.thread.IgniteForkJoinPool"/>
202+
</module>
203+
204+
<module name="org.apache.ignite.tools.checkstyle.ClassUsageRestrictionRule">
205+
<property name="className" value="java.util.concurrent.Executors"/>
206+
<property name="factoryMethods" value="newFixedThreadPool, newWorkStealingPool, newSingleThreadExecutor, newCachedThreadPool, unconfigurableExecutorService"/>
207+
<property name="substitutionClassName" value="org.apache.ignite.internal.thread.IgniteThreadPoolExecutor"/>
208+
</module>
209+
210+
<module name="org.apache.ignite.tools.checkstyle.ClassUsageRestrictionRule">
211+
<property name="className" value="java.util.concurrent.Executors"/>
212+
<property name="factoryMethods" value="newSingleThreadScheduledExecutor, newScheduledThreadPool, , unconfigurableScheduledExecutorService"/>
213+
<property name="substitutionClassName" value="org.apache.ignite.internal.thread.IgniteScheduledThreadPoolExecutor"/>
214+
</module>
215+
216+
<module name="org.apache.ignite.tools.checkstyle.ClassUsageRestrictionRule">
217+
<property name="className" value="java.util.concurrent.ThreadPoolExecutor"/>
218+
<property name="substitutionClassName" value="org.apache.ignite.internal.thread.IgniteThreadPoolExecutor"/>
219+
</module>
220+
221+
<module name="org.apache.ignite.tools.checkstyle.ClassUsageRestrictionRule">
222+
<property name="className" value="java.util.concurrent.ScheduledThreadPoolExecutor"/>
223+
<property name="substitutionClassName" value="org.apache.ignite.internal.thread.IgniteScheduledThreadPoolExecutor"/>
224+
</module>
225+
226+
<module name="SuppressionXpathSingleFilter">
227+
<property name="checks" value="org.apache.ignite.tools.checkstyle.ClassUsageRestrictionRule"/>
228+
<property name="files" value="DumpReader\.java|CacheLoadOnlyStoreAdapter\.java|[\\/]io[\\/]opencensus[\\/]|[\\/]jdbc[\\/]thin[\\/]|[\\/]test[\\/]|[\\/]tests[\\/]|[\\/]internal[\\/]client[\\/]|[\\/]org[\\/]apache[\\/]ignite[\\/]internal[\\/]thread[\\/]"/>
229+
</module>
197230
</module>
198231

199232
<!--
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ignite.tools.checkstyle;
19+
20+
import java.util.Arrays;
21+
import java.util.Collections;
22+
import java.util.HashSet;
23+
import java.util.Objects;
24+
import java.util.Set;
25+
import java.util.stream.Collectors;
26+
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27+
import com.puppycrawl.tools.checkstyle.api.DetailAST;
28+
29+
import static com.puppycrawl.tools.checkstyle.api.FullIdent.createFullIdent;
30+
import static com.puppycrawl.tools.checkstyle.api.FullIdent.createFullIdentBelow;
31+
import static com.puppycrawl.tools.checkstyle.api.TokenTypes.EXTENDS_CLAUSE;
32+
import static com.puppycrawl.tools.checkstyle.api.TokenTypes.IMPORT;
33+
import static com.puppycrawl.tools.checkstyle.api.TokenTypes.LITERAL_NEW;
34+
import static com.puppycrawl.tools.checkstyle.api.TokenTypes.METHOD_CALL;
35+
import static com.puppycrawl.tools.checkstyle.api.TokenTypes.METHOD_REF;
36+
import static com.puppycrawl.tools.checkstyle.api.TokenTypes.STATIC_IMPORT;
37+
38+
/** */
39+
public class ClassUsageRestrictionRule extends AbstractCheck {
40+
/** */
41+
private ClassDescriptor restrictedCls;
42+
43+
/** */
44+
private Set<String> restrictedFactoryMethods = Collections.emptySet();
45+
46+
/** */
47+
private String substitutionClsName;
48+
49+
/** */
50+
private boolean isRestrictedClsImportDetected;
51+
52+
/** */
53+
private final Set<String> restrictedStaticMethodImports = new HashSet<>();
54+
55+
/** */
56+
private static final int[] TOKENS = new int[] {
57+
IMPORT,
58+
STATIC_IMPORT,
59+
EXTENDS_CLAUSE,
60+
LITERAL_NEW,
61+
METHOD_REF,
62+
METHOD_CALL
63+
};
64+
65+
@Override public void init() {
66+
if (restrictedCls == null)
67+
throw new IllegalStateException("Restricted class is not set");
68+
69+
if (isEmpty(substitutionClsName))
70+
throw new IllegalStateException("Substitution class is not set");
71+
}
72+
73+
/** {@inheritDoc} */
74+
@Override public void beginTree(DetailAST rootAST) {
75+
isRestrictedClsImportDetected = false;
76+
77+
restrictedStaticMethodImports.clear();
78+
}
79+
80+
/** {@inheritDoc} */
81+
@Override public void visitToken(DetailAST token) {
82+
if (token.getType() == IMPORT) {
83+
String clsImport = createFullIdentBelow(token).getText();
84+
85+
if (Objects.equals(restrictedCls.fullName, clsImport))
86+
isRestrictedClsImportDetected = true;
87+
}
88+
else if (token.getType() == STATIC_IMPORT) {
89+
ClassMemberDescriptor desc = ClassMemberDescriptor.fromToken(token.getFirstChild().getNextSibling());
90+
91+
if (restrictedCls.fullName.equals(desc.clsName))
92+
restrictedStaticMethodImports.add(desc.memberName);
93+
}
94+
else if (token.getType() == EXTENDS_CLAUSE || token.getType() == LITERAL_NEW) {
95+
if (isRestrictedClass(createFullIdentBelow(token).getText()))
96+
logFailure(token);
97+
}
98+
else if (token.getType() == METHOD_REF) {
99+
DetailAST clsToken = token.getFirstChild();
100+
101+
DetailAST methodToken = clsToken.getNextSibling();
102+
103+
if (restrictedFactoryMethods.contains(methodToken.getText()) && isRestrictedClass(createFullIdent(clsToken).getText()))
104+
logFailure(token);
105+
}
106+
else if (token.getType() == METHOD_CALL) {
107+
ClassMemberDescriptor desc = ClassMemberDescriptor.fromToken(token.getFirstChild());
108+
109+
if (
110+
restrictedFactoryMethods.contains(desc.memberName) &&
111+
(restrictedStaticMethodImports.contains(desc.memberName) || isRestrictedClass(desc.clsName))
112+
) {
113+
logFailure(token);
114+
}
115+
}
116+
else
117+
throw new IllegalStateException();
118+
}
119+
120+
/** {@inheritDoc} */
121+
@Override public int[] getDefaultTokens() {
122+
return TOKENS.clone();
123+
}
124+
125+
/** {@inheritDoc} */
126+
@Override public int[] getAcceptableTokens() {
127+
return TOKENS.clone();
128+
}
129+
130+
/** {@inheritDoc} */
131+
@Override public int[] getRequiredTokens() {
132+
return TOKENS.clone();
133+
}
134+
135+
/** */
136+
public void setClassName(String clsName) {
137+
restrictedCls = ClassDescriptor.forName(clsName);
138+
}
139+
140+
/** */
141+
public void setFactoryMethods(String factoryMethods) {
142+
restrictedFactoryMethods = Arrays.stream(factoryMethods.split(",")).map(String::trim).collect(Collectors.toSet());
143+
}
144+
145+
/** */
146+
public void setSubstitutionClassName(String substitutionClsName) {
147+
this.substitutionClsName = substitutionClsName;
148+
}
149+
150+
/** */
151+
private void logFailure(DetailAST token) {
152+
log(
153+
token,
154+
"Usage of {0} class and its factory methods is restricted. Use {1} class as a substitution",
155+
restrictedCls.fullName,
156+
substitutionClsName
157+
);
158+
}
159+
160+
/** */
161+
private static boolean isEmpty(String str) {
162+
return str == null || str.isEmpty();
163+
}
164+
165+
/** */
166+
private boolean isRestrictedClass(String clsName) {
167+
if (Objects.equals(restrictedCls.fullName, clsName))
168+
return true;
169+
170+
return Objects.equals(restrictedCls.simpleName, clsName) &&
171+
(isRestrictedClsImportDetected || "java.lang".equals(restrictedCls.packageName));
172+
}
173+
174+
/** */
175+
private static class ClassDescriptor {
176+
/** */
177+
private final String fullName;
178+
179+
/** */
180+
private final String packageName;
181+
182+
/** */
183+
private final String simpleName;
184+
185+
/** */
186+
public ClassDescriptor(String fullName, String packageName, String simpleName) {
187+
if (isEmpty(fullName) || isEmpty(packageName) || isEmpty(simpleName))
188+
throw new IllegalArgumentException("Invalid class name");
189+
190+
this.fullName = fullName;
191+
this.packageName = packageName;
192+
this.simpleName = simpleName;
193+
}
194+
195+
/** */
196+
public static ClassDescriptor forName(String clsName) {
197+
int sepPos = clsName.lastIndexOf('.');
198+
199+
if (sepPos == -1)
200+
throw new IllegalArgumentException("Invalid class name");
201+
202+
return new ClassDescriptor(clsName, clsName.substring(0, sepPos), clsName.substring(sepPos + 1));
203+
}
204+
}
205+
206+
/** */
207+
private static class ClassMemberDescriptor {
208+
/** */
209+
private final String clsName;
210+
211+
/** */
212+
private final String memberName;
213+
214+
/** */
215+
public ClassMemberDescriptor(String clsName, String memberName) {
216+
this.clsName = clsName;
217+
this.memberName = memberName;
218+
}
219+
220+
/** */
221+
public static ClassMemberDescriptor fromToken(DetailAST token) {
222+
final String text = createFullIdent(token).getText();
223+
224+
final int memberSepPos = text.lastIndexOf('.');
225+
226+
if (memberSepPos == -1)
227+
return new ClassMemberDescriptor("", text);
228+
229+
return new ClassMemberDescriptor(
230+
text.substring(0, memberSepPos),
231+
text.substring(memberSepPos + 1)
232+
);
233+
}
234+
}
235+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ignite.internal.thread.context.concurrent;
19+
20+
import java.util.Collection;
21+
import java.util.List;
22+
import java.util.concurrent.Callable;
23+
import java.util.concurrent.ExecutionException;
24+
import java.util.concurrent.ExecutorService;
25+
import java.util.concurrent.Future;
26+
import java.util.concurrent.TimeUnit;
27+
import java.util.concurrent.TimeoutException;
28+
import org.apache.ignite.internal.thread.context.function.ThreadContextAwareCallable;
29+
import org.apache.ignite.internal.thread.context.function.ThreadContextAwareRunnable;
30+
31+
/** */
32+
public class ThreadContextAwareExecutorService<E extends ExecutorService> implements ExecutorService {
33+
/** */
34+
protected E delegate;
35+
36+
/** */
37+
protected ThreadContextAwareExecutorService() {
38+
// No-op.
39+
}
40+
41+
/** */
42+
public ThreadContextAwareExecutorService(E delegate) {
43+
this.delegate = delegate;
44+
}
45+
46+
/** {@inheritDoc} */
47+
@Override public void shutdown() {
48+
delegate.shutdown();
49+
}
50+
51+
/** {@inheritDoc} */
52+
@Override public List<Runnable> shutdownNow() {
53+
return delegate.shutdownNow();
54+
}
55+
56+
/** {@inheritDoc} */
57+
@Override public boolean isShutdown() {
58+
return delegate.isShutdown();
59+
}
60+
61+
/** {@inheritDoc} */
62+
@Override public boolean isTerminated() {
63+
return delegate.isTerminated();
64+
}
65+
66+
/** {@inheritDoc} */
67+
@Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
68+
return delegate.awaitTermination(timeout, unit);
69+
}
70+
71+
/** {@inheritDoc} */
72+
@Override public <T> Future<T> submit(Callable<T> task) {
73+
return delegate.submit(ThreadContextAwareCallable.wrapIfActiveAttributesPresent(task));
74+
}
75+
76+
/** {@inheritDoc} */
77+
@Override public <T> Future<T> submit(Runnable task, T result) {
78+
return delegate.submit(ThreadContextAwareRunnable.wrapIfActiveAttributesPresent(task), result);
79+
}
80+
81+
/** {@inheritDoc} */
82+
@Override public Future<?> submit(Runnable task) {
83+
return delegate.submit(ThreadContextAwareRunnable.wrapIfActiveAttributesPresent(task));
84+
}
85+
86+
/** {@inheritDoc} */
87+
@Override public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
88+
return delegate.invokeAll(ThreadContextAwareCallable.wrapIfActiveAttributesPresent(tasks));
89+
}
90+
91+
/** {@inheritDoc} */
92+
@Override public <T> List<Future<T>> invokeAll(
93+
Collection<? extends Callable<T>> tasks,
94+
long timeout,
95+
TimeUnit unit
96+
) throws InterruptedException {
97+
return delegate.invokeAll(ThreadContextAwareCallable.wrapIfActiveAttributesPresent(tasks), timeout, unit);
98+
}
99+
100+
/** {@inheritDoc} */
101+
@Override public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
102+
return delegate.invokeAny(ThreadContextAwareCallable.wrapIfActiveAttributesPresent(tasks));
103+
}
104+
105+
/** {@inheritDoc} */
106+
@Override public <T> T invokeAny(
107+
Collection<? extends Callable<T>> tasks,
108+
long timeout,
109+
TimeUnit unit
110+
) throws InterruptedException, ExecutionException, TimeoutException {
111+
return delegate.invokeAny(ThreadContextAwareCallable.wrapIfActiveAttributesPresent(tasks), timeout, unit);
112+
}
113+
114+
/** {@inheritDoc} */
115+
@Override public void execute(Runnable command) {
116+
delegate.execute(ThreadContextAwareRunnable.wrapIfActiveAttributesPresent(command));
117+
}
118+
}

0 commit comments

Comments
 (0)