Skip to content

Commit 9e5d9bd

Browse files
Artem LabazinArtem Labazin
authored andcommitted
Add tests and threads helpers
1 parent bc63791 commit 9e5d9bd

18 files changed

+1221
-8
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1212
- Add more tests.
1313
- Add `JavaDoc`.
1414

15+
## [1.5.0](https://github.com/appulse-projects/utils-java/releases/tag/1.5.0) - 2018-03-22
16+
17+
Added tests and threads helpers.
18+
19+
### Added
20+
21+
- Threads helpers: executor wrappers, customizable thread factory and executor builders.
22+
- Test helpers.
23+
1524
## [1.3.3](https://github.com/appulse-projects/utils-java/releases/tag/1.3.3) - 2018-03-15
1625

1726
Small refactoring.

pom.xml

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ limitations under the License.
2424

2525
<groupId>io.appulse</groupId>
2626
<artifactId>utils-java</artifactId>
27-
<version>1.3.3</version>
27+
<version>1.5.0</version>
2828
<packaging>jar</packaging>
2929

3030
<properties>
@@ -62,7 +62,7 @@ limitations under the License.
6262
<url>https://github.com/appulse-projects/utils-java</url>
6363
<connection>scm:git:https://github.com/appulse-projects/utils-java.git</connection>
6464
<developerConnection>scm:git:https://github.com/appulse-projects/utils-java.git</developerConnection>
65-
<tag>1.3.3</tag>
65+
<tag>1.5.0</tag>
6666
</scm>
6767

6868
<distributionManagement>
@@ -102,6 +102,25 @@ limitations under the License.
102102
<scope>provided</scope>
103103
</dependency>
104104

105+
<dependency>
106+
<groupId>org.slf4j</groupId>
107+
<artifactId>slf4j-api</artifactId>
108+
<version>1.7.25</version>
109+
</dependency>
110+
<dependency>
111+
<groupId>io.appulse</groupId>
112+
<artifactId>logging-java</artifactId>
113+
<version>1.0.2</version>
114+
<scope>provided</scope>
115+
</dependency>
116+
117+
<dependency>
118+
<groupId>junit</groupId>
119+
<artifactId>junit</artifactId>
120+
<version>4.12</version>
121+
<scope>provided</scope>
122+
</dependency>
123+
105124
<dependency>
106125
<groupId>com.google.code.findbugs</groupId>
107126
<artifactId>annotations</artifactId>
@@ -115,12 +134,6 @@ limitations under the License.
115134
<scope>provided</scope>
116135
</dependency>
117136

118-
<dependency>
119-
<groupId>junit</groupId>
120-
<artifactId>junit</artifactId>
121-
<version>4.12</version>
122-
<scope>test</scope>
123-
</dependency>
124137
<dependency>
125138
<groupId>org.assertj</groupId>
126139
<artifactId>assertj-core</artifactId>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appulse.utils.test;
18+
19+
import java.util.LinkedList;
20+
import java.util.List;
21+
22+
import ch.qos.logback.classic.spi.ILoggingEvent;
23+
import ch.qos.logback.core.AppenderBase;
24+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
25+
26+
/**
27+
*
28+
* @author Artem Labazin
29+
* @since 1.5.0
30+
*/
31+
@SuppressFBWarnings("MS_MUTABLE_COLLECTION_PKGPROTECT")
32+
public class TestAppender extends AppenderBase<ILoggingEvent> {
33+
34+
public static final List<ILoggingEvent> EVENTS = new LinkedList<>();
35+
36+
@Override
37+
protected void append (ILoggingEvent eventObject) {
38+
EVENTS.add(eventObject);
39+
}
40+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appulse.utils.test;
18+
19+
import lombok.val;
20+
21+
import org.junit.rules.TestWatcher;
22+
import org.junit.runner.Description;
23+
24+
/**
25+
*
26+
* @author Artem Labazin
27+
* @since 1.5.0
28+
*/
29+
@SuppressWarnings("PMD.SystemPrintln")
30+
public class TestMethodNamePrinter extends TestWatcher {
31+
32+
@Override
33+
protected void starting (Description description) {
34+
val message = String.format("%nRUNNING TEST: %s.%s%n",
35+
description.getClassName(),
36+
description.getMethodName());
37+
System.out.println(message);
38+
}
39+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appulse.utils.threads;
18+
19+
import static java.lang.Integer.MAX_VALUE;
20+
import static java.util.concurrent.TimeUnit.MILLISECONDS;
21+
import static java.util.concurrent.TimeUnit.MINUTES;
22+
23+
import java.util.concurrent.ForkJoinPool;
24+
import java.util.concurrent.SynchronousQueue;
25+
26+
import io.appulse.utils.threads.executor.builder.ExecutorServiceBuilder;
27+
import io.appulse.utils.threads.executor.builder.ForkJoinPoolBuilder;
28+
import io.appulse.utils.threads.executor.builder.ScheduledExecutorServiceBuilder;
29+
30+
/**
31+
*
32+
* @author Artem Labazin
33+
* @since 1.5.0
34+
*/
35+
public final class AppulseExecutors {
36+
37+
public static ExecutorServiceBuilder newFixedThreadPool (int threads) {
38+
return new ExecutorServiceBuilder()
39+
.corePoolSize(threads)
40+
.maxPoolSize(threads)
41+
.keepAliveTime(0L)
42+
.unit(MILLISECONDS);
43+
}
44+
45+
public static ForkJoinPoolBuilder newWorkStealingPool () {
46+
return new ForkJoinPoolBuilder()
47+
.parallelism(Runtime.getRuntime().availableProcessors())
48+
.threadFactory(ForkJoinPool.defaultForkJoinWorkerThreadFactory)
49+
.asyncMode(true);
50+
}
51+
52+
public static ExecutorServiceBuilder newSingleThreadExecutor () {
53+
return new ExecutorServiceBuilder()
54+
.corePoolSize(1)
55+
.maxPoolSize(1)
56+
.keepAliveTime(0L)
57+
.unit(MILLISECONDS);
58+
}
59+
60+
public static ExecutorServiceBuilder newCachedThreadPool () {
61+
return new ExecutorServiceBuilder()
62+
.corePoolSize(0)
63+
.maxPoolSize(MAX_VALUE)
64+
.keepAliveTime(1L)
65+
.unit(MINUTES)
66+
.queue(new SynchronousQueue<>());
67+
}
68+
69+
public static ScheduledExecutorServiceBuilder newScheduledThreadPool () {
70+
return new ScheduledExecutorServiceBuilder();
71+
}
72+
73+
private AppulseExecutors () {
74+
}
75+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appulse.utils.threads;
18+
19+
import static java.lang.Thread.NORM_PRIORITY;
20+
import static java.util.Locale.ROOT;
21+
import static java.util.Optional.ofNullable;
22+
import static lombok.AccessLevel.PRIVATE;
23+
24+
import java.lang.Thread.UncaughtExceptionHandler;
25+
import java.util.Optional;
26+
import java.util.concurrent.Executors;
27+
import java.util.concurrent.ThreadFactory;
28+
import java.util.concurrent.atomic.AtomicLong;
29+
30+
import lombok.Builder;
31+
import lombok.NonNull;
32+
import lombok.experimental.FieldDefaults;
33+
import lombok.val;
34+
35+
/**
36+
*
37+
* @author Artem Labazin
38+
* @since 1.5.0
39+
*/
40+
@SuppressWarnings("PMD.DoNotUseThreads")
41+
@FieldDefaults(level = PRIVATE, makeFinal = true)
42+
public final class AppulseThreadFactory implements ThreadFactory {
43+
44+
ThreadFactory parent;
45+
46+
int priority;
47+
48+
Optional<String> name;
49+
50+
AtomicLong threadCount;
51+
52+
Optional<Boolean> daemon;
53+
54+
Optional<UncaughtExceptionHandler> uncaughtExceptionHandler;
55+
56+
@Builder
57+
public AppulseThreadFactory (String name, Boolean daemon, Integer priority, ThreadFactory parent,
58+
UncaughtExceptionHandler uncaughtExceptionHandler
59+
) {
60+
this.name = ofNullable(name);
61+
threadCount = this.name
62+
.filter(it -> it.contains("%d"))
63+
.map(it -> new AtomicLong(1))
64+
.orElse(null);
65+
66+
this.daemon = ofNullable(daemon);
67+
this.uncaughtExceptionHandler = ofNullable(uncaughtExceptionHandler);
68+
69+
this.priority = ofNullable(priority)
70+
.orElse(NORM_PRIORITY);
71+
72+
this.parent = ofNullable(parent)
73+
.orElse(Executors.defaultThreadFactory());
74+
}
75+
76+
@Override
77+
public Thread newThread (@NonNull Runnable runnable) {
78+
val thread = parent.newThread(runnable);
79+
thread.setPriority(priority);
80+
81+
name.map(it -> ofNullable(threadCount)
82+
.map(counter -> String.format(ROOT, it, counter.getAndIncrement()))
83+
.orElse(it)
84+
)
85+
.ifPresent(thread::setName);
86+
87+
daemon.ifPresent(thread::setDaemon);
88+
uncaughtExceptionHandler.ifPresent(thread::setUncaughtExceptionHandler);
89+
return thread;
90+
}
91+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appulse.utils.threads.executor;
18+
19+
import java.util.concurrent.ExecutorService;
20+
21+
import lombok.NonNull;
22+
import lombok.extern.slf4j.Slf4j;
23+
import lombok.val;
24+
25+
/**
26+
*
27+
* @author Artem Labazin
28+
* @since 1.5.0
29+
*/
30+
@Slf4j
31+
@SuppressWarnings("PMD.DoNotUseThreads")
32+
public final class ExecutorServiceWithClientTrace extends ExecutorServiceWrapper {
33+
34+
public ExecutorServiceWithClientTrace (ExecutorService delegate) {
35+
super(delegate);
36+
}
37+
38+
@Override
39+
public void execute (@NonNull Runnable command) {
40+
val clientStack = new Exception("Client stack trace");
41+
val threadName = Thread.currentThread().getName();
42+
43+
super.execute(() -> {
44+
try {
45+
command.run();
46+
} catch (Exception ex) {
47+
log.error("Exception '{}' in task submitted from thread '{}' here:",
48+
ex.getClass().getSimpleName(), threadName, clientStack);
49+
throw ex;
50+
}
51+
});
52+
}
53+
}

0 commit comments

Comments
 (0)