From 8ec2f25876c0ba6286dec631160e1eb17f09b446 Mon Sep 17 00:00:00 2001 From: Piotr Stawirej Date: Fri, 8 Dec 2023 19:37:16 +0100 Subject: [PATCH] refactor: refactor and tests --- .../multi/MandatoryActionBuilder.java | 13 ++++ .../multi/MultiOptionalBuilder.java | 35 ++++++++++ .../multi/MultiThreadsCollider.java | 38 +++++++++-- .../multi/MultiTimeUnitBuilder.java | 64 +++++++++++++++++++ .../multi/OptionalActionBuilder.java | 14 ++++ .../threadscollider/multi/TimesBuilder.java | 6 ++ .../architecture/Architecture_Scenarios.java | 47 ++++++++++++++ 7 files changed, 211 insertions(+), 6 deletions(-) create mode 100644 src/main/java/pl/amazingcode/threadscollider/multi/MandatoryActionBuilder.java create mode 100644 src/main/java/pl/amazingcode/threadscollider/multi/MultiOptionalBuilder.java create mode 100644 src/main/java/pl/amazingcode/threadscollider/multi/MultiTimeUnitBuilder.java create mode 100644 src/main/java/pl/amazingcode/threadscollider/multi/OptionalActionBuilder.java create mode 100644 src/main/java/pl/amazingcode/threadscollider/multi/TimesBuilder.java create mode 100644 src/test/java/pl/amazingcode/threadscollider/architecture/Architecture_Scenarios.java diff --git a/src/main/java/pl/amazingcode/threadscollider/multi/MandatoryActionBuilder.java b/src/main/java/pl/amazingcode/threadscollider/multi/MandatoryActionBuilder.java new file mode 100644 index 0000000..d77b2a8 --- /dev/null +++ b/src/main/java/pl/amazingcode/threadscollider/multi/MandatoryActionBuilder.java @@ -0,0 +1,13 @@ +package pl.amazingcode.threadscollider.multi; + +/** Builder for mandatory action. */ +public interface MandatoryActionBuilder { + + /** + * Set action to be executed. + * + * @param action action to be executed + * @return builder + */ + TimesBuilder withAction(Runnable action); +} diff --git a/src/main/java/pl/amazingcode/threadscollider/multi/MultiOptionalBuilder.java b/src/main/java/pl/amazingcode/threadscollider/multi/MultiOptionalBuilder.java new file mode 100644 index 0000000..07b3127 --- /dev/null +++ b/src/main/java/pl/amazingcode/threadscollider/multi/MultiOptionalBuilder.java @@ -0,0 +1,35 @@ +package pl.amazingcode.threadscollider.multi; + +import java.util.function.Consumer; +import pl.amazingcode.threadscollider.single.OptionalBuilder; +import pl.amazingcode.threadscollider.single.ThreadsCollider; +import pl.amazingcode.threadscollider.single.TimeUnitBuilder; + +/** Intermediary builder for {@link ThreadsCollider}. */ +public interface MultiOptionalBuilder { + + /** + * Sets exception consumer for threads. This consumer will be called for each exception thrown by + * threads. Consumer will be called in thread safe manner. + * + * @param threadsExceptionsConsumer - exception consumer for threads. + * @return {@link OptionalBuilder} + */ + MultiOptionalBuilder withThreadsExceptionsConsumer(Consumer threadsExceptionsConsumer); + + /** + * Sets await termination timeout for executor service used by {@link ThreadsCollider}. + * + * @param timeout - await termination timeout for executor service used by {@link + * ThreadsCollider}. + * @return {@link TimeUnitBuilder} + */ + MultiTimeUnitBuilder withAwaitTerminationTimeout(long timeout); + + /** + * Builds {@link ThreadsCollider}. + * + * @return {@link ThreadsCollider} + */ + MultiThreadsCollider build(); +} diff --git a/src/main/java/pl/amazingcode/threadscollider/multi/MultiThreadsCollider.java b/src/main/java/pl/amazingcode/threadscollider/multi/MultiThreadsCollider.java index 5f673f5..cf3504e 100644 --- a/src/main/java/pl/amazingcode/threadscollider/multi/MultiThreadsCollider.java +++ b/src/main/java/pl/amazingcode/threadscollider/multi/MultiThreadsCollider.java @@ -12,6 +12,7 @@ import java.util.function.Consumer; import pl.amazingcode.threadscollider.exceptions.ThreadsColliderFailure; +/** Allows to execute multiple actions by all threads at the "same time". */ public final class MultiThreadsCollider implements AutoCloseable { private static final long DEFAULT_TIMEOUT = 60; @@ -47,6 +48,12 @@ private MultiThreadsCollider( this.threadsExceptionsConsumer = threadsExceptionsConsumer; } + /** + * Tries to execute multiple actions by all threads at the "same time". + * + * @throws ThreadsColliderFailure if any exception occurs during execution. This not includes + * exceptions thrown by threads. + */ public void collide() { try { @@ -90,6 +97,7 @@ private void decorate(Runnable runnable) { } } + /** Shuts down the executor service and waits for all threads to finish by given timeout. */ @Override public void close() { try { @@ -107,7 +115,13 @@ private synchronized void consumeException(Exception exception) { threadsExceptionsConsumer.accept(exception); } - public static class MultiThreadsColliderBuilder { + /** Builder for {@link MultiThreadsCollider}. */ + public static class MultiThreadsColliderBuilder + implements MandatoryActionBuilder, + OptionalActionBuilder, + TimesBuilder, + MultiTimeUnitBuilder, + MultiOptionalBuilder { private final List runnables; private final List times; @@ -121,78 +135,90 @@ private MultiThreadsColliderBuilder() { this.times = new ArrayList<>(); } - public static MultiThreadsColliderBuilder multiThreadsCollider() { + public static MandatoryActionBuilder multiThreadsCollider() { return new MultiThreadsColliderBuilder(); } - public MultiThreadsColliderBuilder withAction(Runnable runnable) { + @Override + public TimesBuilder withAction(Runnable runnable) { this.runnables.add(runnable); return this; } + @Override public MultiThreadsColliderBuilder times(int times) { this.times.add(times); return this; } - public MultiThreadsColliderBuilder withAwaitTerminationTimeout(long timeout) { + @Override + public MultiTimeUnitBuilder withAwaitTerminationTimeout(long timeout) { this.timeout = timeout; return this; } - public MultiThreadsColliderBuilder withThreadsExceptionsConsumer( + @Override + public MultiOptionalBuilder withThreadsExceptionsConsumer( Consumer threadsExceptionsConsumer) { this.threadsExceptionsConsumer = threadsExceptionsConsumer; return this; } - public MultiThreadsColliderBuilder asNanoseconds() { + @Override + public MultiOptionalBuilder asNanoseconds() { this.timeUnit = TimeUnit.NANOSECONDS; return this; } + @Override public MultiThreadsColliderBuilder asMicroseconds() { this.timeUnit = TimeUnit.MICROSECONDS; return this; } + @Override public MultiThreadsColliderBuilder asMilliseconds() { this.timeUnit = TimeUnit.MILLISECONDS; return this; } + @Override public MultiThreadsColliderBuilder asSeconds() { this.timeUnit = TimeUnit.SECONDS; return this; } + @Override public MultiThreadsColliderBuilder asMinutes() { this.timeUnit = TimeUnit.MINUTES; return this; } + @Override public MultiThreadsColliderBuilder asHours() { this.timeUnit = TimeUnit.HOURS; return this; } + @Override public MultiThreadsColliderBuilder asDays() { this.timeUnit = TimeUnit.DAYS; return this; } + @Override public MultiThreadsCollider build() { int threadsCount = times.stream().mapToInt(Integer::intValue).sum(); diff --git a/src/main/java/pl/amazingcode/threadscollider/multi/MultiTimeUnitBuilder.java b/src/main/java/pl/amazingcode/threadscollider/multi/MultiTimeUnitBuilder.java new file mode 100644 index 0000000..b8ce0a9 --- /dev/null +++ b/src/main/java/pl/amazingcode/threadscollider/multi/MultiTimeUnitBuilder.java @@ -0,0 +1,64 @@ +package pl.amazingcode.threadscollider.multi; + +import pl.amazingcode.threadscollider.single.Builder; +import pl.amazingcode.threadscollider.single.ThreadsCollider; + +/** Intermediary builder for {@link ThreadsCollider}. */ +public interface MultiTimeUnitBuilder { + + /** + * Sets await termination timeout time unit for executor service used by {@link ThreadsCollider} + * as nanoseconds. + * + * @return {@link Builder} + */ + MultiOptionalBuilder asNanoseconds(); + + /** + * Sets await termination timeout time unit for executor service used by {@link ThreadsCollider} + * as microseconds. + * + * @return {@link Builder} + */ + MultiOptionalBuilder asMicroseconds(); + + /** + * Sets await termination timeout time unit for executor service used by {@link ThreadsCollider} + * as milliseconds. + * + * @return {@link Builder} + */ + MultiOptionalBuilder asMilliseconds(); + + /** + * Sets await termination timeout time unit for executor service used by {@link ThreadsCollider} + * as seconds. + * + * @return {@link Builder} + */ + MultiOptionalBuilder asSeconds(); + + /** + * Sets await termination timeout time unit for executor service used by {@link ThreadsCollider} + * as minutes. + * + * @return {@link Builder} + */ + MultiOptionalBuilder asMinutes(); + + /** + * Sets await termination timeout time unit for executor service used by {@link ThreadsCollider} + * as hours. + * + * @return {@link Builder} + */ + MultiOptionalBuilder asHours(); + + /** + * Sets await termination timeout time unit for executor service used by {@link ThreadsCollider} + * as days. + * + * @return {@link Builder} + */ + MultiOptionalBuilder asDays(); +} diff --git a/src/main/java/pl/amazingcode/threadscollider/multi/OptionalActionBuilder.java b/src/main/java/pl/amazingcode/threadscollider/multi/OptionalActionBuilder.java new file mode 100644 index 0000000..55bbe54 --- /dev/null +++ b/src/main/java/pl/amazingcode/threadscollider/multi/OptionalActionBuilder.java @@ -0,0 +1,14 @@ +package pl.amazingcode.threadscollider.multi; + +import java.util.function.Consumer; + +public interface OptionalActionBuilder { + + TimesBuilder withAction(Runnable action); + + MultiTimeUnitBuilder withAwaitTerminationTimeout(long timeout); + + MultiOptionalBuilder withThreadsExceptionsConsumer(Consumer threadsExceptionsConsumer); + + MultiThreadsCollider build(); +} diff --git a/src/main/java/pl/amazingcode/threadscollider/multi/TimesBuilder.java b/src/main/java/pl/amazingcode/threadscollider/multi/TimesBuilder.java new file mode 100644 index 0000000..4a60aab --- /dev/null +++ b/src/main/java/pl/amazingcode/threadscollider/multi/TimesBuilder.java @@ -0,0 +1,6 @@ +package pl.amazingcode.threadscollider.multi; + +public interface TimesBuilder { + + OptionalActionBuilder times(int times); +} diff --git a/src/test/java/pl/amazingcode/threadscollider/architecture/Architecture_Scenarios.java b/src/test/java/pl/amazingcode/threadscollider/architecture/Architecture_Scenarios.java new file mode 100644 index 0000000..a984a6a --- /dev/null +++ b/src/test/java/pl/amazingcode/threadscollider/architecture/Architecture_Scenarios.java @@ -0,0 +1,47 @@ +package pl.amazingcode.threadscollider.architecture; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.core.importer.ClassFileImporter; +import com.tngtech.archunit.core.importer.ImportOption; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +final class Architecture_Scenarios { + + private static final String ROOT_PACKAGE = "pl.amazingcode.threadscollider"; + private static final String SINGLE_PACKAGE = "pl.amazingcode.threadscollider.single"; + private static final String MULTI_PACKAGE = "pl.amazingcode.threadscollider.multi"; + + private final JavaClasses classes = + new ClassFileImporter() + .withImportOption(new ImportOption.DoNotIncludeTests()) + .importPackages(ROOT_PACKAGE); + + @Test + void Independent_single_package() { + + noClasses() + .that() + .resideInAPackage(SINGLE_PACKAGE) + .should() + .dependOnClassesThat() + .resideInAnyPackage(MULTI_PACKAGE) + .check(classes); + } + + @Test + void Independent_multi_package() { + + noClasses() + .that() + .resideInAPackage(MULTI_PACKAGE) + .should() + .dependOnClassesThat() + .resideInAnyPackage(SINGLE_PACKAGE) + .check(classes); + } +}