Skip to content

Commit 1a06514

Browse files
authored
Merge pull request #378 from ls1intum/feature/disable-thread-group-check
`Development`: Add annotation to disable thread group check
2 parents e0342fc + 99df516 commit 1a06514

File tree

5 files changed

+70
-6
lines changed

5 files changed

+70
-6
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package de.tum.in.test.api;
2+
3+
import static java.lang.annotation.ElementType.*;
4+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
5+
6+
import java.lang.annotation.Documented;
7+
import java.lang.annotation.Inherited;
8+
import java.lang.annotation.Retention;
9+
import java.lang.annotation.Target;
10+
11+
import org.apiguardian.api.API;
12+
13+
/**
14+
* Allows to disable the thread group check for threads which names start with
15+
* any of the given prefixes.
16+
*
17+
* @author Benjamin Schmitz
18+
* @since 1.14.0
19+
* @version 1.0.0
20+
*/
21+
@API(status = API.Status.EXPERIMENTAL)
22+
@Inherited
23+
@Documented
24+
@Retention(RUNTIME)
25+
@Target({ TYPE, ANNOTATION_TYPE })
26+
public @interface DisableThreadGroupCheckFor {
27+
String[] value();
28+
}

src/main/java/de/tum/in/test/api/internal/ConfigurationUtils.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public static AresSecurityConfiguration generateConfiguration(TestContext contex
3333
config.withPackageWhitelist(generatePackageWhiteList(context));
3434
config.withTrustedPackages(getTrustedPackages(context));
3535
config.withThreadTrustScope(getThreadTrustScope(context));
36+
config.withAllowedThreadsInThreadGroup(getAllowedThreadsInThreadGroup(context));
3637
configureAllowLocalPort(config, context);
3738
return config.build();
3839
}
@@ -109,4 +110,9 @@ private static TrustScope getThreadTrustScope(TestContext context) {
109110
return TestContextUtils.findAnnotationIn(context, TrustedThreads.class).map(TrustedThreads::value)
110111
.orElse(TrustScope.MINIMAL);
111112
}
113+
114+
public static Set<String> getAllowedThreadsInThreadGroup(TestContext context) {
115+
return new HashSet<>(TestContextUtils.findAnnotationIn(context, DisableThreadGroupCheckFor.class)
116+
.map(DisableThreadGroupCheckFor::value).map(Arrays::asList).orElse(Collections.emptyList()));
117+
}
112118
}

src/main/java/de/tum/in/test/api/security/AresSecurityConfiguration.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ public final class AresSecurityConfiguration {
2626
private final Set<PackageRule> whitelistedPackages;
2727
private final Set<PackageRule> trustedPackages;
2828
private final TrustScope threadTrustScope;
29+
private final Set<String> allowedThreadsInThreadGroup;
2930

3031
AresSecurityConfiguration(Optional<Class<?>> testClass, Optional<Method> testMethod, Path executionPath, // NOSONAR
3132
Collection<String> whitelistedClassNames, Optional<Collection<PathRule>> whitelistedPaths,
3233
Collection<PathRule> blacklistedPaths, Set<Integer> allowedLocalPorts, OptionalInt allowLocalPortsAbove,
3334
Set<Integer> excludedLocalPorts, OptionalInt allowedThreadCount, Set<PackageRule> blacklistedPackages,
34-
Set<PackageRule> whitelistedPackages, Set<PackageRule> trustedPackages, TrustScope threadTrustScope) {
35+
Set<PackageRule> whitelistedPackages, Set<PackageRule> trustedPackages, TrustScope threadTrustScope,
36+
Set<String> allowedThreadsInThreadGroup) {
3537
this.testClass = Objects.requireNonNull(testClass);
3638
this.testMethod = Objects.requireNonNull(testMethod);
3739
this.executionPath = executionPath.toAbsolutePath();
@@ -46,6 +48,7 @@ public final class AresSecurityConfiguration {
4648
this.whitelistedPackages = Set.copyOf(whitelistedPackages);
4749
this.trustedPackages = Set.copyOf(trustedPackages);
4850
this.threadTrustScope = threadTrustScope;
51+
this.allowedThreadsInThreadGroup = allowedThreadsInThreadGroup;
4952
}
5053

5154
public Optional<Class<?>> testClass() {
@@ -104,6 +107,10 @@ public TrustScope threadTrustScope() {
104107
return threadTrustScope;
105108
}
106109

110+
public Set<String> getAllowedThreadsInThreadGroup() {
111+
return allowedThreadsInThreadGroup;
112+
}
113+
107114
@Override
108115
public boolean equals(Object obj) {
109116
if (this == obj)
@@ -122,24 +129,27 @@ public boolean equals(Object obj) {
122129
&& Objects.equals(blacklistedPaths, other.blacklistedPaths)
123130
&& Objects.equals(blacklistedPackages, other.blacklistedPackages)
124131
&& Objects.equals(whitelistedPackages, other.whitelistedPackages)
125-
&& Objects.equals(threadTrustScope, other.threadTrustScope);
132+
&& Objects.equals(threadTrustScope, other.threadTrustScope)
133+
&& Objects.equals(allowedThreadsInThreadGroup, other.allowedThreadsInThreadGroup);
126134
}
127135

128136
@Override
129137
public int hashCode() {
130138
return Objects.hash(executionPath, testClass, testMethod, whitelistedClassNames, allowedThreadCount,
131-
whitelistedPaths, blacklistedPaths, blacklistedPackages, whitelistedPackages, threadTrustScope);
139+
whitelistedPaths, blacklistedPaths, blacklistedPackages, whitelistedPackages, threadTrustScope,
140+
allowedThreadsInThreadGroup);
132141
}
133142

134143
@Override
135144
public String toString() {
136145
return String.format("AresSecurityConfiguration [whitelistedClassNames=%s, executionPath=%s," //$NON-NLS-1$
137146
+ " testClass=%s, testMethod=%s, whitelistedPaths=%s, blacklistedPaths=%s, allowedLocalPorts=%s," //$NON-NLS-1$
138147
+ " allowLocalPortsAbove=%s, excludedLocalPorts=%s, allowedThreadCount=%s," //$NON-NLS-1$
139-
+ " blacklistedPackages=%s, whitelistedPackages=%s, trustedPackages=%s, threadTrustScope=%s]", //$NON-NLS-1$
148+
+ " blacklistedPackages=%s, whitelistedPackages=%s, trustedPackages=%s, threadTrustScope=%s," //$NON-NLS-1$
149+
+ " allowedThreadsInThreadGroup=%b]", //$NON-NLS-1$
140150
whitelistedClassNames, executionPath, testClass, testMethod, whitelistedPaths, blacklistedPaths,
141151
allowedLocalPorts, allowLocalPortsAbove, excludedLocalPorts, allowedThreadCount, blacklistedPackages,
142-
whitelistedPackages, trustedPackages, threadTrustScope);
152+
whitelistedPackages, trustedPackages, threadTrustScope, allowedThreadsInThreadGroup);
143153
}
144154

145155
public String shortDesc() {

src/main/java/de/tum/in/test/api/security/AresSecurityConfigurationBuilder.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public final class AresSecurityConfigurationBuilder {
6060
private OptionalInt allowedThreadCount;
6161
private Set<PackageRule> trustedPackages;
6262
private TrustScope threadTrustScope;
63+
private Set<String> allowedThreadsInThreadGroup;
6364

6465
private AresSecurityConfigurationBuilder() {
6566
testClass = Optional.empty();
@@ -74,6 +75,7 @@ private AresSecurityConfigurationBuilder() {
7475
allowedThreadCount = OptionalInt.empty();
7576
trustedPackages = Set.of();
7677
threadTrustScope = TrustScope.MINIMAL;
78+
allowedThreadsInThreadGroup = Set.of();
7779
}
7880

7981
public AresSecurityConfigurationBuilder withPath(Path executionPath) {
@@ -142,12 +144,17 @@ public AresSecurityConfigurationBuilder withThreadTrustScope(TrustScope threadTr
142144
return this;
143145
}
144146

147+
public AresSecurityConfigurationBuilder withAllowedThreadsInThreadGroup(Set allowedThreadsInThreadGroup) {
148+
this.allowedThreadsInThreadGroup = allowedThreadsInThreadGroup;
149+
return this;
150+
}
151+
145152
public AresSecurityConfiguration build() {
146153
validate();
147154
return new AresSecurityConfiguration(testClass, testMethod, executionPath, whitelistedClassNames,
148155
Optional.ofNullable(whitelistedPaths), blacklistedPaths, allowedLocalPorts, allowLocalPortsAbove,
149156
excludedLocalPorts, allowedThreadCount, blacklistedPackages, whitelistedPackages, trustedPackages,
150-
threadTrustScope);
157+
threadTrustScope, allowedThreadsInThreadGroup);
151158
}
152159

153160
private void validate() {

src/main/java/de/tum/in/test/api/security/ArtemisSecurityManager.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,8 @@ private Thread[] checkThreadGroup() {
602602
for (Thread thread : threads) {
603603
if (thread == null)
604604
continue;
605+
if (checkIfThreadNameStartsWithAny(thread, configuration.getAllowedThreadsInThreadGroup()))
606+
continue;
605607
try {
606608
thread.interrupt();
607609
thread.join(500 / originalCount + 1L);
@@ -612,6 +614,13 @@ private Thread[] checkThreadGroup() {
612614
}
613615
if (testThreadGroup.activeCount() == 0)
614616
return new Thread[0];
617+
618+
if (Arrays.stream(threads).filter(Thread::isAlive)
619+
.allMatch(t -> checkIfThreadNameStartsWithAny(t, configuration.getAllowedThreadsInThreadGroup()))) {
620+
LOG.debug("All threads in the test thread group are allowed to run."); //$NON-NLS-1$
621+
return new Thread[0];
622+
}
623+
615624
// try forceful shutdown
616625
var securityException = new SecurityException(
617626
localized("security.error_threads_not_stoppable", Arrays.toString(threads))); //$NON-NLS-1$
@@ -657,6 +666,10 @@ private Thread[] checkThreadGroup() {
657666
return threads;
658667
}
659668

669+
private boolean checkIfThreadNameStartsWithAny(Thread thread, Set<String> allowedThreadStarts) {
670+
return allowedThreadStarts.stream().anyMatch(thread.getName()::startsWith);
671+
}
672+
660673
private void checkCommonThreadPool() {
661674
var commonPool = ForkJoinPool.commonPool();
662675
if (commonPool.isQuiescent())

0 commit comments

Comments
 (0)