Skip to content

Commit

Permalink
GH-78 Rewrite database to ORMLite JDBC (#116)
Browse files Browse the repository at this point in the history
* Start working on new database system

* Add ParcelWrapper class, add ORMLite repository implementation

* Fix immutable list modification, apply codestyle changes

* Implement Locker ORMLite repository

* Add ItemStorage ORMLite repository implementation

* Add User ORMLite implementation

* Add ParcelContent ORMLite implementation

* Use CompletableFuture#runAsync instead of scheduler one. Delete legacy database classes

* Set more DatabaseManager properties

* Fix integration tests

* Update TODO

* Update version in Gradle

* Fix cache and rename getInstance to getSingleton in persister classes

* Bump spigot-api to 1.21.4

* Disable ORMLite logs

* Tick TODO

* Apply suggestion

* Apply CodeRabbit suggestion regarding remove() method in ParcelRepositoryOrmLite

* Add TestScheduler

* Revert "Use CompletableFuture#runAsync instead of scheduler one.

This *partially* reverts commit 953942a

* Use ConcurrentHashMaps in caches

* Fix unit tests build

* Update src/main/java/com/eternalcode/parcellockers/database/DatabaseManager.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Revert version check

* Update src/main/java/com/eternalcode/parcellockers/locker/repository/LockerRepositoryOrmLite.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Correct order of CRUD operations

* Correct isAsync method

* Make locker positions unique

* Rename warp to entity

* Update MC version in artifact name

* Apply Rollczi's suggestions

* Update src/test/java/com/eternalcode/parcellockers/TestScheduler.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Refactor caching system

* Update src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelWrapper.java

Co-authored-by: DMK <[email protected]>

* Update src/main/java/com/eternalcode/parcellockers/parcel/repository/ParcelWrapper.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/main/java/com/eternalcode/parcellockers/ParcelLockers.java

Co-authored-by: DMK <[email protected]>

* Update src/main/java/com/eternalcode/parcellockers/database/persister/PositionPersister.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/main/java/com/eternalcode/parcellockers/database/persister/PositionPersister.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/main/java/com/eternalcode/parcellockers/content/repository/ParcelContentRepositoryOrmLite.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Apply CodeRabbit suggestions

* Fix build

* Fail-fast if table creation does not succeed

* [ci skip] Assign databaseManager field

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: DMK <[email protected]>
  • Loading branch information
3 people authored Jan 15, 2025
1 parent 5743cb4 commit 989315d
Show file tree
Hide file tree
Showing 48 changed files with 1,303 additions and 955 deletions.
4 changes: 2 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
## 📝 TODO

- [ ] Add support for more databases (H2, PostgreSQL, MongoDB?)
- [x] Add support for more databases (H2, PostgreSQL, MongoDB?)
- [ ] Add translation system, more languages
- [ ] Add delivery codes, so any person knowing the code can pick up the parcel
- [ ] Add delivery time, so the parcel can be picked up only in a specific time
- [ ] Parcel permissions for user
- [ ] Parcel return logic
- [x] Banned parcel items (configurable)
- [ ] Parcel fee
- [ ] Rewrite database to ORMLite (far in future)
- [x] Rewrite database to ORMLite (far in future)
- [ ] Add more parcel types (fragile, etc...)
- [ ] Add option to config for parcel locker space (e.g. max parcels within a locker)
- [ ] Finish GUIs
Expand Down
11 changes: 7 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ repositories {

dependencies {
// minecraft development api
compileOnly("org.spigotmc:spigot-api:1.21.1-R0.1-SNAPSHOT")
compileOnly("org.spigotmc:spigot-api:1.21.4-R0.1-SNAPSHOT")
implementation("net.kyori:adventure-platform-bukkit:4.3.4")
implementation("net.kyori:adventure-text-minimessage:4.18.0")
implementation("dev.rollczi:litecommands-bukkit:3.9.5")
Expand Down Expand Up @@ -57,6 +57,9 @@ dependencies {

// database
implementation("com.zaxxer:HikariCP:6.2.1")
implementation("com.j256.ormlite:ormlite-jdbc:6.1")
implementation("com.h2database:h2:2.3.232")
implementation("org.postgresql:postgresql:42.7.4")

// lombok
compileOnly("org.projectlombok:lombok:1.18.36")
Expand Down Expand Up @@ -122,20 +125,20 @@ tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
options.setIncremental(true)
options.compilerArgs.add("-parameters")
options.release = 17
options.release = 21
}

tasks {
runServer {
minecraftVersion("1.21.1")
minecraftVersion("1.21.4")
}

test {
useJUnitPlatform()
}

shadowJar {
archiveFileName.set("ParcelLockers v${project.version} (MC 1.8.8-1.21.x).jar")
archiveFileName.set("ParcelLockers v${project.version} (MC 1.21.3-1.21.4).jar")

exclude(
"org/intellij/lang/annotations/**",
Expand Down
71 changes: 50 additions & 21 deletions src/main/java/com/eternalcode/parcellockers/ParcelLockers.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,43 @@

import com.eternalcode.commons.adventure.AdventureLegacyColorPostProcessor;
import com.eternalcode.commons.adventure.AdventureLegacyColorPreProcessor;
import com.eternalcode.commons.bukkit.scheduler.BukkitSchedulerImpl;
import com.eternalcode.commons.scheduler.Scheduler;
import com.eternalcode.parcellockers.command.handler.InvalidUsageImpl;
import com.eternalcode.parcellockers.command.handler.PermissionMessage;
import com.eternalcode.parcellockers.configuration.ConfigurationManager;
import com.eternalcode.parcellockers.configuration.implementation.PluginConfiguration;
import com.eternalcode.parcellockers.content.repository.ParcelContentRepository;
import com.eternalcode.parcellockers.content.repository.ParcelContentRepositoryImpl;
import com.eternalcode.parcellockers.database.DataSourceFactory;
import com.eternalcode.parcellockers.content.repository.ParcelContentRepositoryOrmLite;
import com.eternalcode.parcellockers.database.DatabaseManager;
import com.eternalcode.parcellockers.gui.implementation.locker.LockerMainGUI;
import com.eternalcode.parcellockers.gui.implementation.remote.MainGUI;
import com.eternalcode.parcellockers.gui.implementation.remote.ParcelListGUI;
import com.eternalcode.parcellockers.itemstorage.repository.ItemStorageRepository;
import com.eternalcode.parcellockers.itemstorage.repository.ItemStorageRepositoryImpl;
import com.eternalcode.parcellockers.itemstorage.repository.ItemStorageRepositoryOrmLite;
import com.eternalcode.parcellockers.locker.Locker;
import com.eternalcode.parcellockers.locker.argument.ParcelLockerArgument;
import com.eternalcode.parcellockers.locker.controller.LockerBreakController;
import com.eternalcode.parcellockers.locker.controller.LockerInteractionController;
import com.eternalcode.parcellockers.locker.controller.LockerPlaceController;
import com.eternalcode.parcellockers.locker.repository.LockerRepositoryImpl;
import com.eternalcode.parcellockers.locker.repository.LockerCache;
import com.eternalcode.parcellockers.locker.repository.LockerRepositoryOrmLite;
import com.eternalcode.parcellockers.notification.NotificationAnnouncer;
import com.eternalcode.parcellockers.parcel.Parcel;
import com.eternalcode.parcellockers.parcel.ParcelManager;
import com.eternalcode.parcellockers.parcel.command.ParcelCommand;
import com.eternalcode.parcellockers.parcel.command.argument.ParcelArgument;
import com.eternalcode.parcellockers.parcel.command.argument.ParcelLockerArgument;
import com.eternalcode.parcellockers.parcel.repository.ParcelRepository;
import com.eternalcode.parcellockers.parcel.repository.ParcelRepositoryImpl;
import com.eternalcode.parcellockers.parcel.repository.ParcelCache;
import com.eternalcode.parcellockers.parcel.repository.ParcelRepositoryOrmLite;
import com.eternalcode.parcellockers.updater.UpdaterService;
import com.eternalcode.parcellockers.user.LoadUserController;
import com.eternalcode.parcellockers.user.PrepareUserController;
import com.eternalcode.parcellockers.user.UserManager;
import com.eternalcode.parcellockers.user.UserRepository;
import com.eternalcode.parcellockers.user.UserRepositoryImpl;
import com.eternalcode.parcellockers.user.repository.UserRepository;
import com.eternalcode.parcellockers.user.repository.UserRepositoryOrmLite;
import com.google.common.base.Stopwatch;
import com.zaxxer.hikari.HikariDataSource;
import com.j256.ormlite.logger.LoggerFactory;
import com.j256.ormlite.logger.NullLogBackend;
import dev.rollczi.litecommands.LiteCommands;
import dev.rollczi.litecommands.adventure.LiteAdventureExtension;
import dev.rollczi.litecommands.annotations.LiteCommandsAnnotations;
Expand All @@ -54,6 +58,7 @@
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;

import java.sql.SQLException;
import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
Expand All @@ -71,6 +76,8 @@ public final class ParcelLockers extends JavaPlugin {

private Economy economy;

private DatabaseManager databaseManager;

@Override
public void onEnable() {
Stopwatch started = Stopwatch.createStarted();
Expand All @@ -87,6 +94,7 @@ public void onEnable() {
ConfigurationManager configManager = new ConfigurationManager(this.getDataFolder());
PluginConfiguration config = configManager.load(new PluginConfiguration());
Server server = this.getServer();
Scheduler scheduler = new BukkitSchedulerImpl(this);

if (config.settings.enableSentry) {
Sentry.init(options -> {
Expand All @@ -97,37 +105,54 @@ public void onEnable() {
options.setTag("serverVersion", this.getServer().getVersion());
options.setTag("serverSoftware", PaperLib.getEnvironment().getName());
options.setTag("plugins", Arrays.stream(server.getPluginManager().getPlugins()).toList().toString());
options.setEnabled(false);
this.getLogger().info("Sentry initialized successfully!");
});
}

HikariDataSource dataSource = DataSourceFactory.buildHikariDataSource(config, this.getDataFolder());
LoggerFactory.setLogBackendFactory(new NullLogBackend.NullLogBackendFactory());

DatabaseManager databaseManager = new DatabaseManager(config, this.getLogger(), this.getDataFolder());
this.databaseManager = databaseManager;

try {
databaseManager.connect();
}
catch (SQLException exception) {
this.getLogger().severe("Could not connect to database! Some functions may not work properly!");
throw new RuntimeException(exception);
}

this.skullAPI = LiteSkullFactory.builder()
.cacheExpireAfterWrite(Duration.ofMinutes(45L))
.bukkitScheduler(this)
.threadPool(20)
.build();

LockerRepositoryImpl lockerRepository = new LockerRepositoryImpl(dataSource);
LockerCache lockerCache = new LockerCache();
ParcelCache parcelCache = new ParcelCache();

LockerRepositoryOrmLite lockerRepository = new LockerRepositoryOrmLite(databaseManager, scheduler, lockerCache);
lockerRepository.updateCaches();
ItemStorageRepository itemStorageRepository = new ItemStorageRepositoryImpl(dataSource);

ParcelRepository parcelRepository = new ParcelRepositoryImpl(dataSource);
ParcelRepositoryOrmLite parcelRepository = new ParcelRepositoryOrmLite(databaseManager, scheduler, parcelCache);
parcelRepository.updateCaches();

ParcelManager parcelManager = new ParcelManager(config, announcer, parcelRepository);

UserRepository userRepository = new UserRepositoryImpl(dataSource);
ItemStorageRepository itemStorageRepository = new ItemStorageRepositoryOrmLite(databaseManager, scheduler);

UserRepository userRepository = new UserRepositoryOrmLite(databaseManager, scheduler);
UserManager userManager = new UserManager(userRepository);

ParcelContentRepository parcelContentRepository = new ParcelContentRepositoryImpl(dataSource);
ParcelContentRepository parcelContentRepository = new ParcelContentRepositoryOrmLite(databaseManager, scheduler);

MainGUI mainGUI = new MainGUI(this, server, miniMessage, config, parcelRepository, lockerRepository, userManager);
ParcelListGUI parcelListGUI = new ParcelListGUI(this, server, miniMessage, config, parcelRepository, lockerRepository, userManager, mainGUI);

this.liteCommands = LiteBukkitFactory.builder("parcellockers", this)
.argument(Parcel.class, new ParcelArgument(parcelRepository))
.argument(Locker.class, new ParcelLockerArgument(lockerRepository))
.argument(Parcel.class, new ParcelArgument(parcelCache))
.argument(Locker.class, new ParcelLockerArgument(lockerCache))
.extension(new LiteAdventureExtension<>())
.message(LiteBukkitMessages.PLAYER_ONLY, config.messages.onlyForPlayers)
.commands(LiteCommandsAnnotations.of(
Expand All @@ -147,9 +172,9 @@ public void onEnable() {
LockerMainGUI lockerMainGUI = new LockerMainGUI(this, miniMessage, config, itemStorageRepository, parcelRepository, lockerRepository, announcer, parcelContentRepository, userRepository, this.skullAPI);

Stream.of(
new LockerInteractionController(lockerRepository, lockerMainGUI),
new LockerInteractionController(lockerCache, lockerMainGUI),
new LockerPlaceController(config, this, lockerRepository, announcer),
new LockerBreakController(lockerRepository, announcer, config.messages),
new LockerBreakController(lockerRepository, lockerCache, announcer, config.messages),
new PrepareUserController(userManager),
new LoadUserController(userManager, server)
).forEach(controller -> server.getPluginManager().registerEvents(controller, this));
Expand All @@ -163,6 +188,10 @@ public void onEnable() {

@Override
public void onDisable() {
if (this.databaseManager != null) {
this.databaseManager.disconnect();
}

if (this.liteCommands != null) {
this.liteCommands.unregister();
}
Expand All @@ -181,7 +210,7 @@ private void softwareCheck() {
if (!environment.isPaper()) {
logger.warning("Your server running on unsupported software, please use Paper or its forks");
logger.warning("You can easily download Paper from https://papermc.io/downloads");
logger.warning("WARNING: Supported MC versions are 1.17.x-1.19.x");
logger.warning("WARNING: Supported MC versions are 1.17.x-1.21.x");
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ public interface ParcelContentRepository {

CompletableFuture<Void> save(ParcelContent parcelContent);

CompletableFuture<Void> remove(UUID uniqueId);

CompletableFuture<Void> update(ParcelContent parcelContent);
CompletableFuture<Integer> remove(UUID uniqueId);

CompletableFuture<Optional<ParcelContent>> find(UUID uniqueId);

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.eternalcode.parcellockers.content.repository;

import com.eternalcode.commons.scheduler.Scheduler;
import com.eternalcode.parcellockers.content.ParcelContent;
import com.eternalcode.parcellockers.database.DatabaseManager;
import com.eternalcode.parcellockers.database.wrapper.AbstractRepositoryOrmLite;
import com.j256.ormlite.table.TableUtils;
import io.sentry.Sentry;

import java.sql.SQLException;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

public class ParcelContentRepositoryOrmLite extends AbstractRepositoryOrmLite implements ParcelContentRepository {

public ParcelContentRepositoryOrmLite(DatabaseManager databaseManager, Scheduler scheduler) {
super(databaseManager, scheduler);

try {
TableUtils.createTableIfNotExists(databaseManager.connectionSource(), ParcelContentWrapper.class);
} catch (SQLException exception) {
Sentry.captureException(exception);
throw new RuntimeException("Failed to create ParcelContent table", exception);
}
}

@Override
public CompletableFuture<Void> save(ParcelContent parcelContent) {
return this.saveIfNotExist(ParcelContentWrapper.class, ParcelContentWrapper.from(parcelContent)).thenApply(dao -> null);
}

@Override
public CompletableFuture<Integer> remove(UUID uniqueId) {
return this.deleteById(ParcelContentWrapper.class, uniqueId);
}

@Override
public CompletableFuture<Optional<ParcelContent>> find(UUID uniqueId) {
return this.select(ParcelContentWrapper.class, uniqueId).thenApply(parcelContentWrapper -> Optional.ofNullable(parcelContentWrapper.toParcelContent()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.eternalcode.parcellockers.content.repository;

import com.eternalcode.parcellockers.content.ParcelContent;
import com.eternalcode.parcellockers.database.persister.ItemStackPersister;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import org.bukkit.inventory.ItemStack;

import java.util.List;
import java.util.UUID;

@DatabaseTable(tableName = "parcel_content")
class ParcelContentWrapper {

@DatabaseField(id = true)
private UUID uniqueId;

@DatabaseField(persisterClass = ItemStackPersister.class)
private List<ItemStack> content;

ParcelContentWrapper() {
}

ParcelContentWrapper(UUID uniqueId, List<ItemStack> content) {
this.uniqueId = uniqueId;
this.content = content;
}

static ParcelContentWrapper from(ParcelContent parcelContent) {
return new ParcelContentWrapper(parcelContent.uniqueId(), parcelContent.items());
}

ParcelContent toParcelContent() {
return new ParcelContent(this.uniqueId, this.content);
}
}
Loading

0 comments on commit 989315d

Please sign in to comment.