diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java index ba4273d1..a7625be5 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java @@ -32,7 +32,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; import example.app.caching.inline.async.client.model.GolfTournament; import example.app.caching.inline.async.client.model.support.GolfCourseBuilder; import example.app.caching.inline.async.client.model.support.GolferBuilder; -import example.app.caching.inline.async.client.service.GolfTournamentService; +import example.app.caching.inline.async.client.service.PgaTourService; import example.app.caching.inline.async.config.AsyncInlineCachingConfiguration; import example.app.caching.inline.async.config.AsyncInlineCachingRegionConfiguration; @@ -55,8 +55,9 @@ import example.app.caching.inline.async.config.AsyncInlineCachingRegionConfigura * @see example.app.caching.inline.async.client.model.GolfCourse * @see example.app.caching.inline.async.client.model.GolfTournament * @see example.app.caching.inline.async.client.model.Golfer - * @see example.app.caching.inline.async.client.service.GolfTournamentService + * @see example.app.caching.inline.async.client.service.PgaTourService * @see example.app.caching.inline.async.config.AsyncInlineCachingConfiguration + * @see example.app.caching.inline.async.config.AsyncInlineCachingRegionConfiguration * @since 1.4.0 */ @SpringBootApplication @@ -74,7 +75,7 @@ public class BootGeodeAsyncInlineCachingClientApplication { static class GolfApplicationConfiguration { @Bean - ApplicationRunner runGolfTournament(GolfTournamentService golfTournamentService) { + ApplicationRunner runGolfTournament(PgaTourService pgaTourService) { return args -> { @@ -84,7 +85,7 @@ public class BootGeodeAsyncInlineCachingClientApplication { .buildPairings() .play(); - golfTournamentService.manage(golfTournament); + pgaTourService.manage(golfTournament); }; } diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/model/GolfTournament.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/model/GolfTournament.java index 764bd52c..65a01efd 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/model/GolfTournament.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/model/GolfTournament.java @@ -23,12 +23,14 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.StreamSupport; import org.springframework.data.gemfire.util.ArrayUtils; import org.springframework.data.gemfire.util.CollectionUtils; import org.springframework.util.Assert; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -53,31 +55,23 @@ public class GolfTournament implements Iterable { private GolfCourse golfCourse; - private final List pairings = new ArrayList<>(); + private final List pairings = Collections.synchronizedList(new ArrayList<>()); - private final Set players = new HashSet<>(); + private final Set players = Collections.synchronizedSet(new HashSet<>()); - @Override - public Iterator iterator() { - return Collections.unmodifiableList(this.pairings).iterator(); + public Iterable getPlayers() { + return Collections.unmodifiableSet(this.players); } public boolean isFinished() { - Set finishedPairings = new HashSet<>(this.pairings.size()); - for (Pairing pairing : this) { if (pairing.getHole() < 18) { return false; } - else { - finishedPairings.add(pairing); - } } - this.pairings.removeAll(finishedPairings); - - return this.pairings.isEmpty(); + return true; } public GolfTournament at(GolfCourse golfCourse) { @@ -109,6 +103,11 @@ public class GolfTournament implements Iterable { return this; } + @Override + public Iterator iterator() { + return Collections.unmodifiableList(this.pairings).iterator(); + } + public GolfTournament play() { Assert.state(this.golfCourse != null, "No golf course was declared"); @@ -134,26 +133,39 @@ public class GolfTournament implements Iterable { @Getter @ToString + @EqualsAndHashCode @RequiredArgsConstructor(staticName = "of") public static class Pairing { + private final AtomicBoolean signedScorecard = new AtomicBoolean(false); + @NonNull private final Golfer playerOne; @NonNull private final Golfer playerTwo; - public int getHole() { - return getPlayerOne().getHole(); - } - - public void setHole(int hole) { + public synchronized void setHole(int hole) { this.playerOne.setHole(hole); this.playerTwo.setHole(hole); } - public int playNextHole() { + public synchronized int getHole() { + return getPlayerOne().getHole(); + } + + public boolean in(@NonNull Golfer golfer) { + return this.playerOne.equals(golfer) || this.playerTwo.equals(golfer); + } + + public synchronized int nextHole() { return getHole() + 1; } + + public synchronized boolean signScorecard() { + + return getHole() >= 18 + && this.signedScorecard.compareAndSet(false, true); + } } } diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/GolfTournamentService.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/PgaTourService.java similarity index 86% rename from spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/GolfTournamentService.java rename to spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/PgaTourService.java index f8786773..1486c2a2 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/GolfTournamentService.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/PgaTourService.java @@ -22,6 +22,8 @@ import java.util.Random; import java.util.Set; import java.util.function.Function; +import javax.annotation.PreDestroy; + import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.scheduling.annotation.Scheduled; @@ -45,7 +47,7 @@ import example.app.caching.inline.async.client.model.Golfer; */ @Service @SuppressWarnings("unused") -public class GolfTournamentService implements Closeable { +public class PgaTourService implements Closeable { protected static final int SCORE_DELTA_BOUND = 2; @@ -55,7 +57,7 @@ public class GolfTournamentService implements Closeable { private final Random random = new Random(System.currentTimeMillis()); - public GolfTournamentService(GolferService golferService) { + public PgaTourService(GolferService golferService) { Assert.notNull(golferService, "GolferService must not be null"); @@ -66,12 +68,23 @@ public class GolfTournamentService implements Closeable { return Optional.ofNullable(this.golfTournament); } - @Override + @Override @PreDestroy public void close() { this.golfTournament = null; } - public GolfTournamentService manage(GolfTournament golfTournament) { + public boolean isFinished() { + + GolfTournament golfTournament = this.golfTournament; + + return golfTournament == null || golfTournament.isFinished(); + } + + public boolean isNotFinished() { + return !isFinished(); + } + + public PgaTourService manage(GolfTournament golfTournament) { GolfTournament currentGolfTournament = this.golfTournament; @@ -90,13 +103,13 @@ public class GolfTournamentService implements Closeable { GolfTournament golfTournament = this.golfTournament; - if (golfTournament != null) { + if (isNotFinished()) { playHole(golfTournament); finish(golfTournament); } } - private void playHole(@NonNull GolfTournament golfTournament) { + private synchronized void playHole(@NonNull GolfTournament golfTournament) { GolfCourse golfCourse = golfTournament.getGolfCourse(); @@ -104,7 +117,7 @@ public class GolfTournamentService implements Closeable { for (GolfTournament.Pairing pairing : golfTournament) { - int hole = pairing.playNextHole(); + int hole = pairing.nextHole(); if (!occupiedHoles.contains(hole)) { if (golfCourse.isValidHoleNumber(hole)) { @@ -150,11 +163,8 @@ public class GolfTournamentService implements Closeable { private void finish(@NonNull GolfTournament golfTournament) { - if (golfTournament.isFinished()) { - - GolfCourse golfCourse = golfTournament.getGolfCourse(); - - for (GolfTournament.Pairing pairing : golfTournament) { + for (GolfTournament.Pairing pairing : golfTournament) { + if (pairing.signScorecard()) { updateScore(this::calculateFinalScore, pairing.getPlayerOne()); updateScore(this::calculateFinalScore, pairing.getPlayerTwo()); } diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolfTournamentController.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolferController.java similarity index 76% rename from spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolfTournamentController.java rename to spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolferController.java index 0be02968..e7054350 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolfTournamentController.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolferController.java @@ -25,43 +25,33 @@ import org.springframework.web.bind.annotation.RestController; import example.app.caching.inline.async.client.model.GolfTournament; import example.app.caching.inline.async.client.model.Golfer; -import example.app.caching.inline.async.client.service.GolfTournamentService; import example.app.caching.inline.async.client.service.GolferService; /** - * Spring Web MVC {@link RestController} used to present a view of a {@link GolfTournament} running. + * Spring Web MVC {@link RestController} used to present a view of {@link Golfer Golfers} standings + * when playing in a {@link GolfTournament} on the {@literal PGA TOUR}. * * @author John Blum - * @see example.app.caching.inline.async.client.model.Golfer - * @see example.app.caching.inline.async.client.model.GolfTournament - * @see example.app.caching.inline.async.client.service.GolferService - * @see example.app.caching.inline.async.client.service.GolfTournamentService * @see org.springframework.web.bind.annotation.GetMapping * @see org.springframework.web.bind.annotation.RequestMapping * @see org.springframework.web.bind.annotation.RestController + * @see example.app.caching.inline.async.client.model.Golfer + * @see example.app.caching.inline.async.client.model.GolfTournament + * @see example.app.caching.inline.async.client.service.GolferService * @since 1.4.0 */ @RestController @RequestMapping("/golf/tournament") @SuppressWarnings("unused") -public class GolfTournamentController { +public class GolferController { private final GolferService golferService; - private final GolfTournamentService golfTournamentService; - - public GolfTournamentController(@NonNull GolferService golferService, - @NonNull GolfTournamentService golfTournamentService) { + public GolferController(@NonNull GolferService golferService) { Assert.notNull(golferService, "GolferService must not be null"); - Assert.notNull(golfTournamentService, "GolfTournamentService must not be null"); this.golferService = golferService; - this.golfTournamentService = golfTournamentService; - } - - protected @NonNull GolfTournamentService getGolfTournamentService() { - return this.golfTournamentService; } protected @NonNull GolferService getGolferService() { diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java index 365a5d5d..725cc380 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java @@ -48,10 +48,11 @@ public class AsyncInlineCachingConfiguration { @Bean @Profile("queue-batch-size") AsyncInlineCachingRegionConfigurer batchSizeAsyncInlineCachingConfigurer( - @Value("${spring.geode.sample.async-inline-caching.queue.batch-size:4}") int queueBatchSize, + @Value("${spring.geode.sample.async-inline-caching.queue.batch-size:25}") int queueBatchSize, GolferRepository golferRepository) { return AsyncInlineCachingRegionConfigurer.create(golferRepository, GOLFERS_REGION_NAME) + .withQueueBatchConflationEnabled() .withQueueBatchSize(queueBatchSize) .withQueueBatchTimeInterval(Duration.ofMinutes(60)) .withQueueDispatcherThreadCount(1);