From 615d48d04bef925c8d4bb36d2454b9f3a619d12e Mon Sep 17 00:00:00 2001 From: Jakubk15 <77227023+Jakubk15@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:07:24 +0100 Subject: [PATCH 1/4] Refactor parcel service package structure and update method signatures for clarity --- .../parcellockers/ParcelLockers.java | 8 +-- .../command/debug/DebugCommand.java | 2 +- .../parcellockers/gui/GuiManager.java | 4 +- .../parcellockers/locker/LockerManager.java | 2 +- .../parcel/repository/ParcelRepository.java | 20 +++---- .../repository/ParcelRepositoryOrmLite.java | 57 +++++++------------ .../{ => service}/ParcelDispatchService.java | 5 +- .../parcel/{ => service}/ParcelService.java | 7 ++- .../{ => service}/ParcelServiceImpl.java | 19 +++---- .../parcel/task/ParcelSendTask.java | 2 +- .../ParcelRepositoryIntegrationTest.java | 8 +-- 11 files changed, 56 insertions(+), 78 deletions(-) rename src/main/java/com/eternalcode/parcellockers/parcel/{ => service}/ParcelDispatchService.java (97%) rename src/main/java/com/eternalcode/parcellockers/parcel/{ => service}/ParcelService.java (83%) rename src/main/java/com/eternalcode/parcellockers/parcel/{ => service}/ParcelServiceImpl.java (95%) diff --git a/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java b/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java index 4c25b658b..6c3187abf 100644 --- a/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java +++ b/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java @@ -30,12 +30,12 @@ import com.eternalcode.parcellockers.locker.validation.LockerValidationService; import com.eternalcode.parcellockers.locker.validation.LockerValidator; import com.eternalcode.parcellockers.notification.NoticeService; -import com.eternalcode.parcellockers.parcel.ParcelDispatchService; -import com.eternalcode.parcellockers.parcel.ParcelService; -import com.eternalcode.parcellockers.parcel.ParcelServiceImpl; import com.eternalcode.parcellockers.parcel.ParcelStatus; import com.eternalcode.parcellockers.parcel.command.ParcelCommand; import com.eternalcode.parcellockers.parcel.repository.ParcelRepositoryOrmLite; +import com.eternalcode.parcellockers.parcel.service.ParcelDispatchService; +import com.eternalcode.parcellockers.parcel.service.ParcelService; +import com.eternalcode.parcellockers.parcel.service.ParcelServiceImpl; import com.eternalcode.parcellockers.parcel.task.ParcelSendTask; import com.eternalcode.parcellockers.updater.UpdaterService; import com.eternalcode.parcellockers.user.UserManager; @@ -57,7 +57,6 @@ import java.io.File; import java.sql.SQLException; import java.time.Duration; -import java.util.ArrayList; import java.util.stream.Stream; import net.kyori.adventure.text.minimessage.MiniMessage; import net.milkbowl.vault.economy.Economy; @@ -202,7 +201,6 @@ public void onEnable() { new UpdaterService(this.getPluginMeta().getVersion()); parcelRepository.fetchAll().thenAccept(optionalParcels -> optionalParcels - .orElseGet(ArrayList::new) .stream() .filter(parcel -> parcel.status() != ParcelStatus.DELIVERED) .forEach(parcel -> deliveryRepository.fetch(parcel.uuid()).thenAccept(optionalDelivery -> diff --git a/src/main/java/com/eternalcode/parcellockers/command/debug/DebugCommand.java b/src/main/java/com/eternalcode/parcellockers/command/debug/DebugCommand.java index 7320269d0..9366d2845 100644 --- a/src/main/java/com/eternalcode/parcellockers/command/debug/DebugCommand.java +++ b/src/main/java/com/eternalcode/parcellockers/command/debug/DebugCommand.java @@ -7,7 +7,7 @@ import com.eternalcode.parcellockers.itemstorage.ItemStorageManager; import com.eternalcode.parcellockers.locker.LockerManager; import com.eternalcode.parcellockers.notification.NoticeService; -import com.eternalcode.parcellockers.parcel.ParcelService; +import com.eternalcode.parcellockers.parcel.service.ParcelService; import dev.rollczi.litecommands.annotations.argument.Arg; import dev.rollczi.litecommands.annotations.command.Command; import dev.rollczi.litecommands.annotations.context.Sender; diff --git a/src/main/java/com/eternalcode/parcellockers/gui/GuiManager.java b/src/main/java/com/eternalcode/parcellockers/gui/GuiManager.java index e547b21b8..f2c2faded 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/GuiManager.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/GuiManager.java @@ -9,8 +9,8 @@ import com.eternalcode.parcellockers.locker.Locker; import com.eternalcode.parcellockers.locker.LockerManager; import com.eternalcode.parcellockers.parcel.Parcel; -import com.eternalcode.parcellockers.parcel.ParcelDispatchService; -import com.eternalcode.parcellockers.parcel.ParcelService; +import com.eternalcode.parcellockers.parcel.service.ParcelDispatchService; +import com.eternalcode.parcellockers.parcel.service.ParcelService; import com.eternalcode.parcellockers.shared.Page; import com.eternalcode.parcellockers.shared.PageResult; import com.eternalcode.parcellockers.user.User; diff --git a/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java b/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java index 426ae7998..15c155cf1 100644 --- a/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java +++ b/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java @@ -159,7 +159,7 @@ public CompletableFuture deleteAll(CommandSender sender, NoticeService not } public CompletableFuture isLockerFull(UUID lockerUuid) { - return this.parcelRepository.countByDestinationLocker(lockerUuid) + return this.parcelRepository.countDeliveredParcelsByDestinationLocker(lockerUuid) .thenApply(count -> count > 0 && count >= this.config.settings.maxParcelsPerLocker); } } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java index fccea6270..306f631ee 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java @@ -15,27 +15,25 @@ public interface ParcelRepository { CompletableFuture update(Parcel parcel); - CompletableFuture>> fetchAll(); + CompletableFuture> fetchAll(); - CompletableFuture> fetchById(UUID uuid); + CompletableFuture> findById(UUID uuid); @TestOnly - CompletableFuture>> fetchBySender(UUID sender); + CompletableFuture> findBySender(UUID sender); - CompletableFuture> fetchBySender(UUID sender, Page page); + CompletableFuture> findBySender(UUID sender, Page page); @TestOnly - CompletableFuture>> fetchByReceiver(UUID receiver); + CompletableFuture> findByReceiver(UUID receiver); - CompletableFuture> fetchByReceiver(UUID receiver, Page page); + CompletableFuture> findByReceiver(UUID receiver, Page page); - CompletableFuture countByDestinationLocker(UUID destinationLocker); + CompletableFuture countDeliveredParcelsByDestinationLocker(UUID destinationLocker); - CompletableFuture delete(Parcel parcel); + CompletableFuture delete(Parcel parcel); - CompletableFuture delete(UUID uuid); + CompletableFuture delete(UUID uuid); CompletableFuture deleteAll(); - - CompletableFuture> fetchPage(Page page); } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java index f368989b3..e0d3fbf34 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java @@ -43,38 +43,38 @@ public CompletableFuture update(Parcel parcel) { } @Override - public CompletableFuture> fetchById(UUID uuid) { + public CompletableFuture> findById(UUID uuid) { return this.selectSafe(ParcelTable.class, uuid).thenApply(optional -> optional.map(ParcelTable::toParcel)); } @Override - public CompletableFuture>> fetchBySender(UUID sender) { + public CompletableFuture> findBySender(UUID sender) { return this.action( - ParcelTable.class, dao -> Optional.of(dao.queryForEq(SENDER_COLUMN, sender) + ParcelTable.class, dao -> dao.queryForEq(SENDER_COLUMN, sender) .stream() .map(ParcelTable::toParcel) - .toList())); + .toList()); } - public CompletableFuture> fetchBySender(UUID sender, Page page) { - return this.fetchByPaged(sender, page, SENDER_COLUMN); + public CompletableFuture> findBySender(UUID sender, Page page) { + return this.findByPaged(sender, page, SENDER_COLUMN); } @Override - public CompletableFuture>> fetchByReceiver(UUID receiver) { + public CompletableFuture> findByReceiver(UUID receiver) { return this.action( - ParcelTable.class, dao -> Optional.of(dao.queryForEq(RECEIVER_COLUMN, receiver) + ParcelTable.class, dao -> dao.queryForEq(RECEIVER_COLUMN, receiver) .stream() .map(ParcelTable::toParcel) - .toList())); + .toList()); } - public CompletableFuture> fetchByReceiver(UUID receiver, Page page) { - return this.fetchByPaged(receiver, page, RECEIVER_COLUMN); + public CompletableFuture> findByReceiver(UUID receiver, Page page) { + return this.findByPaged(receiver, page, RECEIVER_COLUMN); } @Override - public CompletableFuture countByDestinationLocker(UUID destinationLocker) { + public CompletableFuture countDeliveredParcelsByDestinationLocker(UUID destinationLocker) { return this.action(ParcelTable.class, dao -> { long count = dao.queryBuilder() .where() @@ -86,7 +86,7 @@ public CompletableFuture countByDestinationLocker(UUID destinationLocke }); } - private CompletableFuture> fetchByPaged(UUID key, Page page, String column) { + private CompletableFuture> findByPaged(UUID key, Page page, String column) { return this.action( ParcelTable.class, dao -> { List parcels = dao.queryBuilder() @@ -108,39 +108,20 @@ private CompletableFuture> fetchByPaged(UUID key, Page page, } @Override - public CompletableFuture delete(Parcel parcel) { + public CompletableFuture delete(Parcel parcel) { return this.delete(parcel.uuid()); } @Override - public CompletableFuture delete(UUID uuid) { - return this.deleteById(ParcelTable.class, uuid); + public CompletableFuture delete(UUID uuid) { + return this.deleteById(ParcelTable.class, uuid).thenApply(i -> i > 0); } @Override - public CompletableFuture> fetchPage(Page page) { - return this.action( - ParcelTable.class, dao -> { - List parcels = dao.queryBuilder() - .limit((long) page.getLimit() + 1) - .offset((long) page.getOffset()) - .query() - .stream().map(ParcelTable::toParcel) - .collect(Collectors.toList()); - - boolean hasNext = parcels.size() > page.getLimit(); - if (hasNext) { - parcels.removeLast(); - } - return new PageResult<>(parcels, hasNext); - }); - } - - @Override - public CompletableFuture>> fetchAll() { - return this.selectAll(ParcelTable.class).thenApply(parcels -> Optional.of(parcels.stream() + public CompletableFuture> fetchAll() { + return this.selectAll(ParcelTable.class).thenApply(parcels -> parcels.stream() .map(ParcelTable::toParcel) - .toList())); + .toList()); } @Override diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/ParcelDispatchService.java b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelDispatchService.java similarity index 97% rename from src/main/java/com/eternalcode/parcellockers/parcel/ParcelDispatchService.java rename to src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelDispatchService.java index 8b39bacd7..2a4ab9b1a 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/ParcelDispatchService.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelDispatchService.java @@ -1,4 +1,4 @@ -package com.eternalcode.parcellockers.parcel; +package com.eternalcode.parcellockers.parcel.service; import com.eternalcode.commons.scheduler.Scheduler; import com.eternalcode.parcellockers.configuration.implementation.PluginConfig; @@ -6,6 +6,7 @@ import com.eternalcode.parcellockers.itemstorage.ItemStorageManager; import com.eternalcode.parcellockers.locker.LockerManager; import com.eternalcode.parcellockers.notification.NoticeService; +import com.eternalcode.parcellockers.parcel.Parcel; import com.eternalcode.parcellockers.parcel.task.ParcelSendTask; import java.time.Duration; import java.time.Instant; @@ -67,7 +68,7 @@ public void dispatch(Player sender, Parcel parcel, List items) { return this.itemStorageManager.delete(sender.getUniqueId()) .thenAccept(deleted -> { if (!Boolean.TRUE.equals(deleted)) { - this.parcelService.delete(parcel.uuid()); // Implement this method + this.parcelService.delete(parcel.uuid()); this.noticeService.player(sender.getUniqueId(), messages -> messages.parcel.cannotSend); return; } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/ParcelService.java b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelService.java similarity index 83% rename from src/main/java/com/eternalcode/parcellockers/parcel/ParcelService.java rename to src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelService.java index 2ae52a423..f17a4f25b 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/ParcelService.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelService.java @@ -1,6 +1,7 @@ -package com.eternalcode.parcellockers.parcel; +package com.eternalcode.parcellockers.parcel.service; import com.eternalcode.parcellockers.notification.NoticeService; +import com.eternalcode.parcellockers.parcel.Parcel; import com.eternalcode.parcellockers.shared.Page; import com.eternalcode.parcellockers.shared.PageResult; import java.util.List; @@ -30,8 +31,8 @@ public interface ParcelService { CompletableFuture> getByReceiver(UUID receiver, Page page); - CompletableFuture delete(UUID uuid); + CompletableFuture delete(UUID uuid); - CompletableFuture delete(Parcel parcel); + CompletableFuture delete(Parcel parcel); } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/ParcelServiceImpl.java b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java similarity index 95% rename from src/main/java/com/eternalcode/parcellockers/parcel/ParcelServiceImpl.java rename to src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java index b519952f1..19e69d278 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/ParcelServiceImpl.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java @@ -1,4 +1,4 @@ -package com.eternalcode.parcellockers.parcel; +package com.eternalcode.parcellockers.parcel.service; import static com.eternalcode.parcellockers.util.InventoryUtil.freeSlotsInInventory; @@ -8,6 +8,7 @@ import com.eternalcode.parcellockers.content.ParcelContent; import com.eternalcode.parcellockers.content.repository.ParcelContentRepository; import com.eternalcode.parcellockers.notification.NoticeService; +import com.eternalcode.parcellockers.parcel.Parcel; import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; import com.eternalcode.parcellockers.shared.Page; import com.eternalcode.parcellockers.shared.PageResult; @@ -158,14 +159,12 @@ public CompletableFuture collect(Player player, Parcel parcel) { return CompletableFuture.completedFuture(null); } - // Give items first items.forEach(item -> this.scheduler.run(() -> ItemUtil.giveItem(player, item))); - // Then delete from database, only invalidate cache if successful return this.parcelRepository.delete(parcel) .thenCompose(deleted -> this.parcelContentRepository.delete(parcel.uuid()) .thenAccept(contentDeleted -> { - if (deleted > 0 && contentDeleted > 0) { + if (deleted && contentDeleted > 0) { this.invalidate(parcel); this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.collected); } else { @@ -186,7 +185,7 @@ public CompletableFuture> get(UUID uuid) { if (cached != null) { return CompletableFuture.completedFuture(Optional.of(cached)); } - return this.parcelRepository.fetchById(uuid).thenApply(optional -> { + return this.parcelRepository.findById(uuid).thenApply(optional -> { optional.ifPresent(this::cache); return optional; }); @@ -206,7 +205,7 @@ public CompletableFuture> getBySender(UUID receiver, Page pag return CompletableFuture.completedFuture(new PageResult<>(pageItems, hasNextPage)); } - return this.parcelRepository.fetchBySender(receiver, page) + return this.parcelRepository.findBySender(receiver, page) .thenApply(result -> { result.items().forEach(this::cache); return result; @@ -227,7 +226,7 @@ public CompletableFuture> getByReceiver(UUID receiver, Page p return CompletableFuture.completedFuture(new PageResult<>(pageItems, hasNextPage)); } - return this.parcelRepository.fetchByReceiver(receiver, page) + return this.parcelRepository.findByReceiver(receiver, page) .thenApply(result -> { result.items().forEach(this::cache); return result; @@ -235,9 +234,9 @@ public CompletableFuture> getByReceiver(UUID receiver, Page p } @Override - public CompletableFuture delete(UUID uuid) { + public CompletableFuture delete(UUID uuid) { return this.parcelRepository.delete(uuid).thenApply(deleted -> { - if (deleted > 0) { + if (deleted) { Parcel cached = this.parcelsByUuid.getIfPresent(uuid); if (cached != null) { this.invalidate(cached); @@ -248,7 +247,7 @@ public CompletableFuture delete(UUID uuid) { } @Override - public CompletableFuture delete(Parcel parcel) { + public CompletableFuture delete(Parcel parcel) { return this.delete(parcel.uuid()); } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java b/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java index 4759d8d6b..43bbc01a2 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java @@ -2,8 +2,8 @@ import com.eternalcode.parcellockers.delivery.DeliveryManager; import com.eternalcode.parcellockers.parcel.Parcel; -import com.eternalcode.parcellockers.parcel.ParcelService; import com.eternalcode.parcellockers.parcel.ParcelStatus; +import com.eternalcode.parcellockers.parcel.service.ParcelService; import java.util.logging.Logger; import org.bukkit.scheduler.BukkitRunnable; diff --git a/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java b/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java index d85e25149..496378b92 100644 --- a/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java +++ b/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java @@ -56,15 +56,15 @@ void test() { parcelRepository.save(new Parcel(uuid, sender, "name", "description", true, receiver, ParcelSize.SMALL, entryLocker, destinationLocker, ParcelStatus.SENT)); - Optional parcel = this.await(parcelRepository.fetchById(uuid)); + Optional parcel = this.await(parcelRepository.findById(uuid)); assertTrue(parcel.isPresent()); assertEquals(uuid, parcel.get().uuid()); - List byReceiver = this.await(parcelRepository.fetchByReceiver(receiver)).orElse(Collections.emptyList()); + List byReceiver = this.await(parcelRepository.findByReceiver(receiver)).orElse(Collections.emptyList()); assertEquals(1, byReceiver.size()); assertEquals(uuid, byReceiver.getFirst().uuid()); - List bySender = this.await(parcelRepository.fetchBySender(sender)).orElse(Collections.emptyList()); + List bySender = this.await(parcelRepository.findBySender(sender)).orElse(Collections.emptyList()); assertEquals(1, bySender.size()); assertEquals(uuid, bySender.getFirst().uuid()); @@ -73,7 +73,7 @@ void test() { assertEquals(uuid, pageResult.items().getFirst().uuid()); this.await(parcelRepository.delete(uuid)); - Optional removedParcel = this.await(parcelRepository.fetchById(uuid)); + Optional removedParcel = this.await(parcelRepository.findById(uuid)); assertTrue(removedParcel.isEmpty()); } From 029fa6672f7aea2df3ec01c042b1999b6f534680 Mon Sep 17 00:00:00 2001 From: Jakubk15 <77227023+Jakubk15@users.noreply.github.com> Date: Mon, 29 Dec 2025 14:09:47 +0100 Subject: [PATCH 2/4] Refactor repository methods to use 'find' instead of 'fetch' for consistency and clarity + codebase decluttering --- .../parcellockers/ParcelLockers.java | 4 +- .../implementation/MessageConfig.java | 4 + .../content/ParcelContentManager.java | 6 +- .../repository/ParcelContentRepository.java | 4 +- .../ParcelContentRepositoryOrmLite.java | 6 +- .../delivery/DeliveryManager.java | 14 +- .../repository/DeliveryRepository.java | 6 +- .../repository/DeliveryRepositoryOrmLite.java | 12 +- .../implementation/remote/ParcelListGui.java | 2 +- .../parcellockers/locker/LockerManager.java | 24 +-- .../locker/repository/LockerRepository.java | 9 +- .../repository/LockerRepositoryOrmLite.java | 13 +- .../parcel/repository/ParcelRepository.java | 2 +- .../repository/ParcelRepositoryOrmLite.java | 55 ++++-- .../parcel/service/ParcelServiceImpl.java | 165 +++++++----------- .../LockerRepositoryIntegrationTest.java | 10 +- .../ParcelRepositoryIntegrationTest.java | 11 +- 17 files changed, 150 insertions(+), 197 deletions(-) diff --git a/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java b/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java index 6c3187abf..cfb973a0a 100644 --- a/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java +++ b/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java @@ -200,10 +200,10 @@ public void onEnable() { new Metrics(this, 17677); new UpdaterService(this.getPluginMeta().getVersion()); - parcelRepository.fetchAll().thenAccept(optionalParcels -> optionalParcels + parcelRepository.findAll().thenAccept(optionalParcels -> optionalParcels .stream() .filter(parcel -> parcel.status() != ParcelStatus.DELIVERED) - .forEach(parcel -> deliveryRepository.fetch(parcel.uuid()).thenAccept(optionalDelivery -> + .forEach(parcel -> deliveryRepository.find(parcel.uuid()).thenAccept(optionalDelivery -> optionalDelivery.ifPresent(delivery -> { long delay = Math.max(0, delivery.deliveryTimestamp().toEpochMilli() - System.currentTimeMillis()); scheduler.runLaterAsync(new ParcelSendTask(parcel, parcelService, deliveryManager), Duration.ofMillis(delay)); diff --git a/src/main/java/com/eternalcode/parcellockers/configuration/implementation/MessageConfig.java b/src/main/java/com/eternalcode/parcellockers/configuration/implementation/MessageConfig.java index 6efac7e01..531bd961d 100644 --- a/src/main/java/com/eternalcode/parcellockers/configuration/implementation/MessageConfig.java +++ b/src/main/java/com/eternalcode/parcellockers/configuration/implementation/MessageConfig.java @@ -74,6 +74,10 @@ public static class ParcelMessages extends OkaeriConfig { .chat("&4✘ &cAn error occurred while collecting the parcel.") .sound(SoundEventKeys.ENTITY_VILLAGER_NO) .build(); + public Notice databaseError = Notice.builder() + .chat("&4✘ &cA database error occurred. Please contact an administrator.") + .sound(SoundEventKeys.ENTITY_ITEM_BREAK) + .build(); public Notice noInventorySpace = Notice.builder() .chat("&4✘ &cYou don't have enough space in your inventory to collect the parcel!") .sound(SoundEventKeys.ENTITY_VILLAGER_NO) diff --git a/src/main/java/com/eternalcode/parcellockers/content/ParcelContentManager.java b/src/main/java/com/eternalcode/parcellockers/content/ParcelContentManager.java index 133d42c4f..d1c59bfe7 100644 --- a/src/main/java/com/eternalcode/parcellockers/content/ParcelContentManager.java +++ b/src/main/java/com/eternalcode/parcellockers/content/ParcelContentManager.java @@ -31,7 +31,7 @@ public CompletableFuture> get(UUID parcelId) { if (content != null) { return CompletableFuture.completedFuture(Optional.of(content)); } - return this.contentRepository.fetch(parcelId).thenApply(optional -> { + return this.contentRepository.find(parcelId).thenApply(optional -> { optional.ifPresent(value -> this.cache.put(parcelId, value)); return optional; }); @@ -52,9 +52,9 @@ public ParcelContent create(UUID parcel, List items) { } public CompletableFuture delete(UUID parcel) { - return this.contentRepository.delete(parcel).thenApply(i -> { + return this.contentRepository.delete(parcel).thenApply(success -> { this.cache.invalidate(parcel); - return i > 0; + return success; }); } diff --git a/src/main/java/com/eternalcode/parcellockers/content/repository/ParcelContentRepository.java b/src/main/java/com/eternalcode/parcellockers/content/repository/ParcelContentRepository.java index 966bdcb61..fdb59c2ff 100644 --- a/src/main/java/com/eternalcode/parcellockers/content/repository/ParcelContentRepository.java +++ b/src/main/java/com/eternalcode/parcellockers/content/repository/ParcelContentRepository.java @@ -9,9 +9,9 @@ public interface ParcelContentRepository { CompletableFuture save(ParcelContent parcelContent); - CompletableFuture> fetch(UUID uniqueId); + CompletableFuture> find(UUID uniqueId); - CompletableFuture delete(UUID uniqueId); + CompletableFuture delete(UUID uniqueId); CompletableFuture deleteAll(); diff --git a/src/main/java/com/eternalcode/parcellockers/content/repository/ParcelContentRepositoryOrmLite.java b/src/main/java/com/eternalcode/parcellockers/content/repository/ParcelContentRepositoryOrmLite.java index 091100531..2ffb57498 100644 --- a/src/main/java/com/eternalcode/parcellockers/content/repository/ParcelContentRepositoryOrmLite.java +++ b/src/main/java/com/eternalcode/parcellockers/content/repository/ParcelContentRepositoryOrmLite.java @@ -29,8 +29,8 @@ public CompletableFuture save(ParcelContent parcelContent) { } @Override - public CompletableFuture delete(UUID uniqueId) { - return this.deleteById(ParcelContentTable.class, uniqueId); + public CompletableFuture delete(UUID uniqueId) { + return this.deleteById(ParcelContentTable.class, uniqueId).thenApply(i -> i > 0); } @Override @@ -39,7 +39,7 @@ public CompletableFuture deleteAll() { } @Override - public CompletableFuture> fetch(UUID uniqueId) { + public CompletableFuture> find(UUID uniqueId) { return this.selectSafe(ParcelContentTable.class, uniqueId) .thenApply(optional -> optional.map(ParcelContentTable::toParcelContent)); } diff --git a/src/main/java/com/eternalcode/parcellockers/delivery/DeliveryManager.java b/src/main/java/com/eternalcode/parcellockers/delivery/DeliveryManager.java index 99406c2ef..2cacfa988 100644 --- a/src/main/java/com/eternalcode/parcellockers/delivery/DeliveryManager.java +++ b/src/main/java/com/eternalcode/parcellockers/delivery/DeliveryManager.java @@ -28,16 +28,12 @@ public DeliveryManager(DeliveryRepository deliveryRepository) { this.cacheAll(); } - public Delivery getOrCreate(UUID parcel, Instant deliveryTimestamp) { - return this.deliveryCache.get(parcel, key -> this.create(key, deliveryTimestamp)); - } - public CompletableFuture> get(UUID parcel) { Delivery cached = this.deliveryCache.getIfPresent(parcel); if (cached != null) { return CompletableFuture.completedFuture(Optional.of(cached)); } - return this.deliveryRepository.fetch(parcel).thenApply(optional -> { + return this.deliveryRepository.find(parcel).thenApply(optional -> { optional.ifPresent(delivery -> this.deliveryCache.put(parcel, delivery)); return optional; }); @@ -54,9 +50,9 @@ public Delivery create(UUID parcel, Instant deliveryTimestamp) { } public CompletableFuture delete(UUID parcel) { - return this.deliveryRepository.delete(parcel).thenApply(i -> { + return this.deliveryRepository.delete(parcel).thenApply(success -> { this.deliveryCache.invalidate(parcel); - return i > 0; + return success; }); } @@ -72,7 +68,7 @@ public CompletableFuture deleteAll(CommandSender sender, NoticeService not } private void cacheAll() { - this.deliveryRepository.fetchAll() - .thenAccept(all -> all.ifPresent(list -> list.forEach(delivery -> this.deliveryCache.put(delivery.parcel(), delivery)))); + this.deliveryRepository.findAll() + .thenAccept(list -> list.forEach(delivery -> this.deliveryCache.put(delivery.parcel(), delivery))); } } diff --git a/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepository.java b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepository.java index 72ff214a4..e8e581cc1 100644 --- a/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepository.java +++ b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepository.java @@ -10,11 +10,11 @@ public interface DeliveryRepository { CompletableFuture save(Delivery delivery); - CompletableFuture> fetch(UUID parcel); + CompletableFuture> find(UUID parcel); - CompletableFuture delete(UUID parcel); + CompletableFuture delete(UUID parcel); CompletableFuture deleteAll(); - CompletableFuture>> fetchAll(); + CompletableFuture> findAll(); } diff --git a/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepositoryOrmLite.java b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepositoryOrmLite.java index fd2a7355d..6f7207f64 100644 --- a/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepositoryOrmLite.java +++ b/src/main/java/com/eternalcode/parcellockers/delivery/repository/DeliveryRepositoryOrmLite.java @@ -29,21 +29,21 @@ public CompletableFuture save(Delivery delivery) { } @Override - public CompletableFuture> fetch(UUID parcel) { + public CompletableFuture> find(UUID parcel) { return this.selectSafe(DeliveryTable.class, parcel) .thenApply(optional -> optional.map(DeliveryTable::toDelivery)); } @Override - public CompletableFuture>> fetchAll() { - return this.selectAll(DeliveryTable.class).thenApply(parcels -> Optional.of(parcels.stream() + public CompletableFuture> findAll() { + return this.selectAll(DeliveryTable.class).thenApply(parcels -> parcels.stream() .map(DeliveryTable::toDelivery) - .toList())); + .toList()); } @Override - public CompletableFuture delete(UUID parcel) { - return this.deleteById(DeliveryTable.class, parcel); + public CompletableFuture delete(UUID parcel) { + return this.deleteById(DeliveryTable.class, parcel).thenApply(i -> i > 0); } @Override diff --git a/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/ParcelListGui.java b/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/ParcelListGui.java index 229c0de94..deb3d2717 100644 --- a/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/ParcelListGui.java +++ b/src/main/java/com/eternalcode/parcellockers/gui/implementation/remote/ParcelListGui.java @@ -119,7 +119,7 @@ private void setupStaticItems(Player player, PaginatedGui gui) { gui.setItem(49, closeItem); } - // fixme: parcels not appearing when there are many deliveries? + // todo: check parcels not appearing when there are many deliveries? private CompletableFuture createParcelItemAsync(Parcel parcel, ConfigItem item) { CompletableFuture> loreFuture = PlaceholderUtil.replaceParcelPlaceholdersAsync( parcel, item.lore(), this.guiManager diff --git a/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java b/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java index 15c155cf1..b55b1c6c6 100644 --- a/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java +++ b/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java @@ -62,7 +62,7 @@ public CompletableFuture> get(UUID uniqueId) { return CompletableFuture.completedFuture(Optional.of(locker)); } - return this.lockerRepository.fetch(uniqueId).thenApply(optionalLocker -> { + return this.lockerRepository.find(uniqueId).thenApply(optionalLocker -> { optionalLocker.ifPresent(locker1 -> { this.lockersByUUID.put(locker1.uuid(), locker1); this.lockersByPosition.put(locker1.position(), locker1); @@ -78,7 +78,7 @@ public CompletableFuture> get(Position position) { return CompletableFuture.completedFuture(Optional.of(locker)); } - return this.lockerRepository.fetch(position).thenApply(optionalLocker -> { + return this.lockerRepository.find(position).thenApply(optionalLocker -> { optionalLocker.ifPresent(locker1 -> { this.lockersByUUID.put(locker1.uuid(), locker1); this.lockersByPosition.put(locker1.position(), locker1); @@ -93,21 +93,7 @@ public CompletableFuture> get(Page page) { if (!cached.isEmpty() && page.getOffset() == 0 && !hasNextPage) { return CompletableFuture.completedFuture(new PageResult<>(cached, false)); } - return this.lockerRepository.fetchPage(page); - } - - public CompletableFuture getOrCreate(UUID uniqueId, String name, Position position) { - Locker lockerByUUID = this.lockersByUUID.getIfPresent(uniqueId); - if (lockerByUUID != null) { - return CompletableFuture.completedFuture(lockerByUUID); - } - - Locker lockerByPosition = this.lockersByPosition.getIfPresent(position); - if (lockerByPosition != null) { - return CompletableFuture.completedFuture(lockerByPosition); - } - - return this.create(uniqueId, name, position); + return this.lockerRepository.findPage(page); } public CompletableFuture create(UUID uniqueId, String name, Position position) { @@ -158,8 +144,8 @@ public CompletableFuture deleteAll(CommandSender sender, NoticeService not }); } - public CompletableFuture isLockerFull(UUID lockerUuid) { - return this.parcelRepository.countDeliveredParcelsByDestinationLocker(lockerUuid) + public CompletableFuture isLockerFull(UUID uniqueId) { + return this.parcelRepository.countDeliveredParcelsByDestinationLocker(uniqueId) .thenApply(count -> count > 0 && count >= this.config.settings.maxParcelsPerLocker); } } diff --git a/src/main/java/com/eternalcode/parcellockers/locker/repository/LockerRepository.java b/src/main/java/com/eternalcode/parcellockers/locker/repository/LockerRepository.java index ff339720f..223b7a6c3 100644 --- a/src/main/java/com/eternalcode/parcellockers/locker/repository/LockerRepository.java +++ b/src/main/java/com/eternalcode/parcellockers/locker/repository/LockerRepository.java @@ -4,7 +4,6 @@ import com.eternalcode.parcellockers.shared.Page; import com.eternalcode.parcellockers.shared.PageResult; import com.eternalcode.parcellockers.shared.Position; -import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -13,11 +12,9 @@ public interface LockerRepository { CompletableFuture save(Locker locker); - CompletableFuture>> fetchAll(); + CompletableFuture> find(UUID uuid); - CompletableFuture> fetch(UUID uuid); - - CompletableFuture> fetch(Position position); + CompletableFuture> find(Position position); CompletableFuture delete(UUID uuid); @@ -25,5 +22,5 @@ public interface LockerRepository { CompletableFuture deleteAll(); - CompletableFuture> fetchPage(Page page); + CompletableFuture> findPage(Page page); } diff --git a/src/main/java/com/eternalcode/parcellockers/locker/repository/LockerRepositoryOrmLite.java b/src/main/java/com/eternalcode/parcellockers/locker/repository/LockerRepositoryOrmLite.java index abba33be4..6c5e69568 100644 --- a/src/main/java/com/eternalcode/parcellockers/locker/repository/LockerRepositoryOrmLite.java +++ b/src/main/java/com/eternalcode/parcellockers/locker/repository/LockerRepositoryOrmLite.java @@ -34,19 +34,12 @@ public CompletableFuture save(Locker locker) { } @Override - public CompletableFuture>> fetchAll() { - return this.selectAll(LockerTable.class).thenApply(lockers -> Optional.of(lockers.stream() - .map(LockerTable::toLocker) - .collect(Collectors.toList()))); - } - - @Override - public CompletableFuture> fetch(UUID uuid) { + public CompletableFuture> find(UUID uuid) { return this.selectSafe(LockerTable.class, uuid).thenApply(optional -> optional.map(LockerTable::toLocker)); } @Override - public CompletableFuture> fetch(Position position) { + public CompletableFuture> find(Position position) { return this.action( LockerTable.class, dao -> { List lockers = dao.queryForEq("position", position); @@ -65,7 +58,7 @@ public CompletableFuture delete(Locker locker) { } @Override - public CompletableFuture> fetchPage(Page page) { + public CompletableFuture> findPage(Page page) { return this.action( LockerTable.class, dao -> { List lockers = dao.queryBuilder() diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java index 306f631ee..1997ace4b 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepository.java @@ -15,7 +15,7 @@ public interface ParcelRepository { CompletableFuture update(Parcel parcel); - CompletableFuture> fetchAll(); + CompletableFuture> findAll(); CompletableFuture> findById(UUID uuid); diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java index e0d3fbf34..d92aa0549 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java @@ -10,7 +10,10 @@ import com.eternalcode.parcellockers.shared.exception.DatabaseException; import com.j256.ormlite.table.TableUtils; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -21,6 +24,7 @@ public class ParcelRepositoryOrmLite extends AbstractRepositoryOrmLite implement private static final String RECEIVER_COLUMN = "receiver"; private static final String SENDER_COLUMN = "sender"; private static final String DESTINATION_LOCKER_COLUMN = "destination_locker"; + private static final String STATUS_COLUMN = "status"; public ParcelRepositoryOrmLite(DatabaseManager databaseManager, Scheduler scheduler) { super(databaseManager, scheduler); @@ -34,53 +38,66 @@ public ParcelRepositoryOrmLite(DatabaseManager databaseManager, Scheduler schedu @Override public CompletableFuture save(Parcel parcel) { + Objects.requireNonNull(parcel, "Parcel cannot be null"); return this.saveIfNotExist(ParcelTable.class, ParcelTable.from(parcel)).thenApply(dao -> null); } @Override public CompletableFuture update(Parcel parcel) { + Objects.requireNonNull(parcel, "Parcel cannot be null"); return this.save(ParcelTable.class, ParcelTable.from(parcel)).thenApply(dao -> null); } @Override public CompletableFuture> findById(UUID uuid) { - return this.selectSafe(ParcelTable.class, uuid).thenApply(optional -> optional.map(ParcelTable::toParcel)); + Objects.requireNonNull(uuid, "UUID cannot be null"); + return this.selectSafe(ParcelTable.class, uuid) + .thenApply(optional -> optional.map(ParcelTable::toParcel)); } @Override public CompletableFuture> findBySender(UUID sender) { + Objects.requireNonNull(sender, "Sender UUID cannot be null"); return this.action( - ParcelTable.class, dao -> dao.queryForEq(SENDER_COLUMN, sender) - .stream() - .map(ParcelTable::toParcel) - .toList()); + ParcelTable.class, + dao -> dao.queryForEq(SENDER_COLUMN, sender).stream() + .map(ParcelTable::toParcel) + .toList() + ); } public CompletableFuture> findBySender(UUID sender, Page page) { + Objects.requireNonNull(sender, "Sender UUID cannot be null"); + Objects.requireNonNull(page, "Page cannot be null"); return this.findByPaged(sender, page, SENDER_COLUMN); } @Override public CompletableFuture> findByReceiver(UUID receiver) { + Objects.requireNonNull(receiver, "Receiver UUID cannot be null"); return this.action( - ParcelTable.class, dao -> dao.queryForEq(RECEIVER_COLUMN, receiver) - .stream() - .map(ParcelTable::toParcel) - .toList()); + ParcelTable.class, + dao -> dao.queryForEq(RECEIVER_COLUMN, receiver).stream() + .map(ParcelTable::toParcel) + .toList() + ); } public CompletableFuture> findByReceiver(UUID receiver, Page page) { + Objects.requireNonNull(receiver, "Receiver UUID cannot be null"); + Objects.requireNonNull(page, "Page cannot be null"); return this.findByPaged(receiver, page, RECEIVER_COLUMN); } @Override public CompletableFuture countDeliveredParcelsByDestinationLocker(UUID destinationLocker) { + Objects.requireNonNull(destinationLocker, "Destination locker UUID cannot be null"); return this.action(ParcelTable.class, dao -> { long count = dao.queryBuilder() .where() .eq(DESTINATION_LOCKER_COLUMN, destinationLocker) .and() - .eq("status", ParcelStatus.DELIVERED) + .eq(STATUS_COLUMN, ParcelStatus.DELIVERED) .countOf(); return (int) count; }); @@ -97,31 +114,35 @@ private CompletableFuture> findByPaged(UUID key, Page page, S .query() .stream() .map(ParcelTable::toParcel) - .collect(Collectors.toList()); + .collect(Collectors.toCollection(ArrayList::new)); boolean hasNext = parcels.size() > page.getLimit(); if (hasNext) { parcels.removeLast(); } - return new PageResult<>(parcels, hasNext); + + return new PageResult<>(Collections.unmodifiableList(parcels), hasNext); }); } @Override public CompletableFuture delete(Parcel parcel) { + Objects.requireNonNull(parcel, "Parcel cannot be null"); return this.delete(parcel.uuid()); } @Override public CompletableFuture delete(UUID uuid) { - return this.deleteById(ParcelTable.class, uuid).thenApply(i -> i > 0); + Objects.requireNonNull(uuid, "UUID cannot be null"); + return this.deleteById(ParcelTable.class, uuid).thenApply(rows -> rows > 0); } @Override - public CompletableFuture> fetchAll() { - return this.selectAll(ParcelTable.class).thenApply(parcels -> parcels.stream() - .map(ParcelTable::toParcel) - .toList()); + public CompletableFuture> findAll() { + return this.selectAll(ParcelTable.class) + .thenApply(parcels -> parcels.stream() + .map(ParcelTable::toParcel) + .toList()); } @Override diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java index 19e69d278..cd3352abb 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java @@ -15,8 +15,9 @@ import com.eternalcode.parcellockers.shared.exception.ParcelOperationException; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -import java.util.ArrayList; +import com.google.common.base.Preconditions; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -29,6 +30,11 @@ public class ParcelServiceImpl implements ParcelService { private static final String PARCEL_FEE_BYPASS_PERMISSION = "parcellockers.fee.bypass"; + private static final String PLACEHOLDER_AMOUNT = "{AMOUNT}"; + private static final String PLACEHOLDER_COUNT = "{COUNT}"; + + private static final long CACHE_EXPIRE_HOURS = 3; + private static final long CACHE_MAX_SIZE = 10_000; private final NoticeService noticeService; private final ParcelRepository parcelRepository; @@ -38,14 +44,14 @@ public class ParcelServiceImpl implements ParcelService { private final Economy economy; private final Cache parcelsByUuid; - private final Cache> parcelsBySender; - private final Cache> parcelsByReceiver; public ParcelServiceImpl( NoticeService noticeService, ParcelRepository parcelRepository, ParcelContentRepository parcelContentRepository, - Scheduler scheduler, PluginConfig config, Economy economy + Scheduler scheduler, + PluginConfig config, + Economy economy ) { this.noticeService = noticeService; this.parcelRepository = parcelRepository; @@ -55,24 +61,22 @@ public ParcelServiceImpl( this.economy = economy; this.parcelsByUuid = Caffeine.newBuilder() - .expireAfterAccess(3, TimeUnit.HOURS) - .maximumSize(10_000) - .build(); - - this.parcelsBySender = Caffeine.newBuilder() - .expireAfterAccess(3, TimeUnit.HOURS) - .maximumSize(10_000) - .build(); - - this.parcelsByReceiver = Caffeine.newBuilder() - .expireAfterAccess(3, TimeUnit.HOURS) - .maximumSize(10_000) + .expireAfterAccess(CACHE_EXPIRE_HOURS, TimeUnit.HOURS) + .maximumSize(CACHE_MAX_SIZE) .build(); - } @Override public CompletableFuture send(Player sender, Parcel parcel, List items) { + Objects.requireNonNull(sender, "Sender cannot be null"); + Objects.requireNonNull(parcel, "Parcel cannot be null"); + Objects.requireNonNull(items, "Items list cannot be null"); + Preconditions.checkArgument(!items.isEmpty(), "Items list cannot be empty"); + + List itemsCopy = items.stream() + .map(ItemStack::clone) + .toList(); + if (!sender.hasPermission(PARCEL_FEE_BYPASS_PERMISSION)) { double fee = switch (parcel.size()) { case SMALL -> this.config.settings.smallParcelFee; @@ -82,11 +86,13 @@ public CompletableFuture send(Player sender, Parcel parcel, List 0) { boolean success = this.economy.withdrawPlayer(sender, fee).transactionSuccess(); + String formattedFee = String.format("%.2f", fee); + if (!success) { this.noticeService.create() .notice(messages -> messages.parcel.insufficientFunds) .player(sender.getUniqueId()) - .placeholder("{AMOUNT}", String.format("%.2f", fee)) + .placeholder(PLACEHOLDER_AMOUNT, formattedFee) .send(); return CompletableFuture.completedFuture(false); } @@ -94,23 +100,21 @@ public CompletableFuture send(Player sender, Parcel parcel, List messages.parcel.feeWithdrawn) .player(sender.getUniqueId()) - .placeholder("{AMOUNT}", String.format("%.2f", fee)) + .placeholder(PLACEHOLDER_AMOUNT, formattedFee) .send(); } } - // Save parcel first, then content. If content fails, delete parcel (transactional behavior) return this.parcelRepository.save(parcel) - .thenCompose(unused -> this.parcelContentRepository.save(new ParcelContent(parcel.uuid(), items)) + .thenCompose(unused -> this.parcelContentRepository.save(new ParcelContent(parcel.uuid(), itemsCopy)) .thenApply(contentSaved -> { - this.cache(parcel); + this.parcelsByUuid.put(parcel.uuid(), parcel); this.noticeService.player(sender.getUniqueId(), messages -> messages.parcel.sent); return true; }) .exceptionally(contentError -> { - // Rollback: delete parcel if content save failed - this.parcelRepository.delete(parcel.uuid()); this.noticeService.player(sender.getUniqueId(), messages -> messages.parcel.cannotSend); + this.parcelRepository.delete(parcel.uuid()); // TODO handle failure async, ParcelOperationException can be thrown before delete completes throw new ParcelOperationException("Failed to save parcel content, rolled back parcel", contentError); }) ) @@ -122,19 +126,23 @@ public CompletableFuture send(Player sender, Parcel parcel, List update(Parcel updated) { - this.cache(updated); + Objects.requireNonNull(updated, "Updated parcel cannot be null"); + this.parcelsByUuid.put(updated.uuid(), updated); return this.parcelRepository.update(updated); } @Override public CompletableFuture delete(CommandSender sender, Parcel parcel) { + Objects.requireNonNull(sender, "Sender cannot be null"); + Objects.requireNonNull(parcel, "Parcel cannot be null"); + return this.parcelRepository.delete(parcel) .thenAccept(unused -> { this.noticeService.create() .notice(messages -> messages.parcel.deleted) .viewer(sender) .send(); - this.invalidate(parcel); + this.parcelsByUuid.invalidate(parcel.uuid()); }) .exceptionally(throwable -> { this.noticeService.create() @@ -147,7 +155,10 @@ public CompletableFuture delete(CommandSender sender, Parcel parcel) { @Override public CompletableFuture collect(Player player, Parcel parcel) { - return this.parcelContentRepository.fetch(parcel.uuid()).thenCompose(optional -> { + Objects.requireNonNull(player, "Player cannot be null"); + Objects.requireNonNull(parcel, "Parcel cannot be null"); + + return this.parcelContentRepository.find(parcel.uuid()).thenCompose(optional -> { if (optional.isEmpty()) { this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.cannotCollect); return CompletableFuture.completedFuture(null); @@ -164,12 +175,13 @@ public CompletableFuture collect(Player player, Parcel parcel) { return this.parcelRepository.delete(parcel) .thenCompose(deleted -> this.parcelContentRepository.delete(parcel.uuid()) .thenAccept(contentDeleted -> { - if (deleted && contentDeleted > 0) { - this.invalidate(parcel); - this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.collected); - } else { - this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.cannotCollect); + if (!deleted || !contentDeleted) { + this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.databaseError); + return; } + this.parcelsByUuid.invalidate(parcel.uuid()); + this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.collected); + // FIXME: Delete failuje → itemy dodane, ale content pozostaje }) ) .exceptionally(throwable -> { @@ -181,65 +193,52 @@ public CompletableFuture collect(Player player, Parcel parcel) { @Override public CompletableFuture> get(UUID uuid) { + Objects.requireNonNull(uuid, "UUID cannot be null"); + Parcel cached = this.parcelsByUuid.getIfPresent(uuid); if (cached != null) { return CompletableFuture.completedFuture(Optional.of(cached)); } return this.parcelRepository.findById(uuid).thenApply(optional -> { - optional.ifPresent(this::cache); + optional.ifPresent(parcel -> this.parcelsByUuid.put(parcel.uuid(), parcel)); return optional; }); } @Override - public CompletableFuture> getBySender(UUID receiver, Page page) { - List cached = this.parcelsBySender.getIfPresent(receiver); - - if (cached != null) { - int fromIndex = Math.min(page.getOffset(), cached.size()); - int toIndex = Math.min(page.getOffset() + page.getLimit(), cached.size()); + public CompletableFuture> getBySender(UUID sender, Page page) { + Objects.requireNonNull(sender, "Sender UUID cannot be null"); + Objects.requireNonNull(page, "Page cannot be null"); - List pageItems = cached.subList(fromIndex, toIndex); - boolean hasNextPage = toIndex < cached.size(); - - return CompletableFuture.completedFuture(new PageResult<>(pageItems, hasNextPage)); - } - - return this.parcelRepository.findBySender(receiver, page) + return this.parcelRepository.findBySender(sender, page) .thenApply(result -> { - result.items().forEach(this::cache); + result.items().forEach(parcel -> this.parcelsByUuid.put(parcel.uuid(), parcel)); return result; }); } @Override public CompletableFuture> getByReceiver(UUID receiver, Page page) { - List cached = this.parcelsByReceiver.getIfPresent(receiver); - - if (cached != null) { - int fromIndex = Math.min(page.getOffset(), cached.size()); - int toIndex = Math.min(page.getOffset() + page.getLimit(), cached.size()); - - List pageItems = cached.subList(fromIndex, toIndex); - boolean hasNextPage = toIndex < cached.size(); - - return CompletableFuture.completedFuture(new PageResult<>(pageItems, hasNextPage)); - } + Objects.requireNonNull(receiver, "Receiver UUID cannot be null"); + Objects.requireNonNull(page, "Page cannot be null"); return this.parcelRepository.findByReceiver(receiver, page) .thenApply(result -> { - result.items().forEach(this::cache); + result.items().forEach(parcel -> this.parcelsByUuid.put(parcel.uuid(), parcel)); return result; }); } @Override public CompletableFuture delete(UUID uuid) { + Objects.requireNonNull(uuid, "UUID cannot be null"); return this.parcelRepository.delete(uuid).thenApply(deleted -> { if (deleted) { Parcel cached = this.parcelsByUuid.getIfPresent(uuid); if (cached != null) { - this.invalidate(cached); + this.parcelsByUuid.invalidate(cached.uuid()); + } else { + this.parcelsByUuid.invalidate(uuid); } } return deleted; @@ -248,59 +247,23 @@ public CompletableFuture delete(UUID uuid) { @Override public CompletableFuture delete(Parcel parcel) { + Objects.requireNonNull(parcel, "Parcel cannot be null"); return this.delete(parcel.uuid()); } @Override public CompletableFuture deleteAll(CommandSender sender, NoticeService noticeService) { + Objects.requireNonNull(sender, "Sender cannot be null"); + Objects.requireNonNull(noticeService, "NoticeService cannot be null"); + return this.parcelRepository.deleteAll().thenAccept(deleted -> { noticeService.create() .notice(messages -> messages.admin.deletedParcels) .viewer(sender) - .placeholder("{COUNT}", deleted.toString()) + .placeholder(PLACEHOLDER_COUNT, deleted.toString()) .send(); this.parcelsByUuid.invalidateAll(); - this.parcelsBySender.invalidateAll(); - this.parcelsByReceiver.invalidateAll(); }); } - - private void cache(Parcel parcel) { - this.parcelsByUuid.put(parcel.uuid(), parcel); - - List bySender = this.parcelsBySender.get(parcel.sender(), k -> new ArrayList<>()); - bySender.add(parcel); - this.parcelsBySender.put(parcel.sender(), bySender); - - List byReceiver = this.parcelsByReceiver.get(parcel.receiver(), k -> new ArrayList<>()); - byReceiver.add(parcel); - this.parcelsByReceiver.put(parcel.receiver(), byReceiver); - } - - - private void invalidate(Parcel parcel) { - this.parcelsByUuid.invalidate(parcel.uuid()); - - // Use getIfPresent to avoid creating empty entries - List bySender = this.parcelsBySender.getIfPresent(parcel.sender()); - if (bySender != null) { - bySender.remove(parcel); - if (bySender.isEmpty()) { - this.parcelsBySender.invalidate(parcel.sender()); - } else { - this.parcelsBySender.put(parcel.sender(), bySender); - } - } - - List byReceiver = this.parcelsByReceiver.getIfPresent(parcel.receiver()); - if (byReceiver != null) { - byReceiver.remove(parcel); - if (byReceiver.isEmpty()) { - this.parcelsByReceiver.invalidate(parcel.receiver()); - } else { - this.parcelsByReceiver.put(parcel.receiver(), byReceiver); - } - } - } } diff --git a/src/test/java/com/eternalcode/parcellockers/database/LockerRepositoryIntegrationTest.java b/src/test/java/com/eternalcode/parcellockers/database/LockerRepositoryIntegrationTest.java index 6c91e9ce7..19bd71ae0 100644 --- a/src/test/java/com/eternalcode/parcellockers/database/LockerRepositoryIntegrationTest.java +++ b/src/test/java/com/eternalcode/parcellockers/database/LockerRepositoryIntegrationTest.java @@ -29,7 +29,7 @@ class LockerRepositoryIntegrationTest extends IntegrationTestSpec { @Container - private static final MySQLContainer mySQLContainer = new MySQLContainer(DockerImageName.parse("mysql:latest")); + private static final MySQLContainer mySQLContainer = new MySQLContainer<>(DockerImageName.parse("mysql:latest")); @TempDir private Path tempDir; @@ -52,20 +52,20 @@ void test() { parcelLockerRepository.save(new Locker(uuid, description, position)); - Optional parcelLocker = this.await(parcelLockerRepository.fetch(uuid)); + Optional parcelLocker = this.await(parcelLockerRepository.find(uuid)); assertTrue(parcelLocker.isPresent()); assertEquals(uuid, parcelLocker.get().uuid()); - Optional byPosition = this.await(parcelLockerRepository.fetch(position)); + Optional byPosition = this.await(parcelLockerRepository.find(position)); assertTrue(byPosition.isPresent()); assertEquals(uuid, byPosition.get().uuid()); - PageResult pageResult = this.await(parcelLockerRepository.fetchPage(new Page(0, 28))); + PageResult pageResult = this.await(parcelLockerRepository.findPage(new Page(0, 28))); assertEquals(1, pageResult.items().size()); assertEquals(uuid, pageResult.items().getFirst().uuid()); this.await(parcelLockerRepository.delete(uuid)); - Optional removed = this.await(parcelLockerRepository.fetch(uuid)); + Optional removed = this.await(parcelLockerRepository.find(uuid)); assertTrue(removed.isEmpty()); } diff --git a/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java b/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java index 496378b92..5eb838d04 100644 --- a/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java +++ b/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java @@ -11,11 +11,8 @@ import com.eternalcode.parcellockers.parcel.ParcelStatus; import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; import com.eternalcode.parcellockers.parcel.repository.ParcelRepositoryOrmLite; -import com.eternalcode.parcellockers.shared.Page; -import com.eternalcode.parcellockers.shared.PageResult; import java.io.File; import java.nio.file.Path; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -60,18 +57,14 @@ void test() { assertTrue(parcel.isPresent()); assertEquals(uuid, parcel.get().uuid()); - List byReceiver = this.await(parcelRepository.findByReceiver(receiver)).orElse(Collections.emptyList()); + List byReceiver = this.await(parcelRepository.findByReceiver(receiver)); assertEquals(1, byReceiver.size()); assertEquals(uuid, byReceiver.getFirst().uuid()); - List bySender = this.await(parcelRepository.findBySender(sender)).orElse(Collections.emptyList()); + List bySender = this.await(parcelRepository.findBySender(sender)); assertEquals(1, bySender.size()); assertEquals(uuid, bySender.getFirst().uuid()); - PageResult pageResult = this.await(parcelRepository.fetchPage(new Page(0, 28))); - assertEquals(1, pageResult.items().size()); - assertEquals(uuid, pageResult.items().getFirst().uuid()); - this.await(parcelRepository.delete(uuid)); Optional removedParcel = this.await(parcelRepository.findById(uuid)); assertTrue(removedParcel.isEmpty()); From 99e349b1a0e593981d47896d097cafdb26440fa2 Mon Sep 17 00:00:00 2001 From: Jakubk15 <77227023+Jakubk15@users.noreply.github.com> Date: Mon, 29 Dec 2025 14:22:04 +0100 Subject: [PATCH 3/4] Adjust to Gemini's suggestions --- .../parcel/service/ParcelServiceImpl.java | 12 ++++++------ .../database/ParcelRepositoryIntegrationTest.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java index cd3352abb..bd9774588 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java @@ -112,10 +112,10 @@ public CompletableFuture send(Player sender, Parcel parcel, List messages.parcel.sent); return true; }) - .exceptionally(contentError -> { + .exceptionallyCompose(contentError -> { this.noticeService.player(sender.getUniqueId(), messages -> messages.parcel.cannotSend); - this.parcelRepository.delete(parcel.uuid()); // TODO handle failure async, ParcelOperationException can be thrown before delete completes - throw new ParcelOperationException("Failed to save parcel content, rolled back parcel", contentError); + return this.parcelRepository.delete(parcel.uuid()) + .thenCompose(deleted -> CompletableFuture.failedFuture(new ParcelOperationException("Failed to save parcel content, rolled back parcel", contentError))); }) ) .exceptionally(throwable -> { @@ -170,8 +170,6 @@ public CompletableFuture collect(Player player, Parcel parcel) { return CompletableFuture.completedFuture(null); } - items.forEach(item -> this.scheduler.run(() -> ItemUtil.giveItem(player, item))); - return this.parcelRepository.delete(parcel) .thenCompose(deleted -> this.parcelContentRepository.delete(parcel.uuid()) .thenAccept(contentDeleted -> { @@ -179,9 +177,11 @@ public CompletableFuture collect(Player player, Parcel parcel) { this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.databaseError); return; } + + items.forEach(item -> this.scheduler.run(() -> ItemUtil.giveItem(player, item))); + this.parcelsByUuid.invalidate(parcel.uuid()); this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.collected); - // FIXME: Delete failuje → itemy dodane, ale content pozostaje }) ) .exceptionally(throwable -> { diff --git a/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java b/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java index 5eb838d04..4dd43db70 100644 --- a/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java +++ b/src/test/java/com/eternalcode/parcellockers/database/ParcelRepositoryIntegrationTest.java @@ -29,7 +29,7 @@ class ParcelRepositoryIntegrationTest extends IntegrationTestSpec { @Container - private static final MySQLContainer mySQLContainer = new MySQLContainer(DockerImageName.parse("mysql:latest")); + private static final MySQLContainer mySQLContainer = new MySQLContainer<>(DockerImageName.parse("mysql:latest")); @TempDir private Path tempDir; From fb19da96b3b4bd2e7ed5773274f22eb2fe721523 Mon Sep 17 00:00:00 2001 From: Jakubk15 <77227023+Jakubk15@users.noreply.github.com> Date: Mon, 29 Dec 2025 16:12:54 +0100 Subject: [PATCH 4/4] Add missing @Override annotations --- build.gradle.kts | 3 ++- .../parcel/repository/ParcelRepositoryOrmLite.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3e3e20c77..026db6cf7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,7 +41,7 @@ dependencies { // gitcheck paperLibrary("com.eternalcode:gitcheck:1.0.0") - // metrics and sentry + // metrics implementation("org.bstats:bstats-bukkit:3.1.0") // database @@ -142,6 +142,7 @@ tasks { mergeServiceFiles() val relocationPrefix = "com.eternalcode.parcellockers.libs" + listOf( "org.bstats" ).forEach { relocate(it, "$relocationPrefix.$it") } diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java index d92aa0549..52d944b0a 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelRepositoryOrmLite.java @@ -66,6 +66,7 @@ public CompletableFuture> findBySender(UUID sender) { ); } + @Override public CompletableFuture> findBySender(UUID sender, Page page) { Objects.requireNonNull(sender, "Sender UUID cannot be null"); Objects.requireNonNull(page, "Page cannot be null"); @@ -83,6 +84,7 @@ public CompletableFuture> findByReceiver(UUID receiver) { ); } + @Override public CompletableFuture> findByReceiver(UUID receiver, Page page) { Objects.requireNonNull(receiver, "Receiver UUID cannot be null"); Objects.requireNonNull(page, "Page cannot be null");